mirror of https://github.com/Nheko-Reborn/nheko
parent
0c798554b5
commit
eafbab6ae1
@ -0,0 +1,309 @@ |
||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors |
||||
// |
||||
// SPDX-License-Identifier: GPL-3.0-or-later |
||||
|
||||
import ".." |
||||
import "../components" |
||||
import QtQuick 2.12 |
||||
import QtQuick.Controls 2.12 |
||||
import QtQuick.Layouts 1.12 |
||||
import im.nheko 1.0 |
||||
|
||||
ApplicationWindow { |
||||
id: win |
||||
|
||||
property ImagePackListModel packlist |
||||
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 2.3) |
||||
property SingleImagePackModel currentPack: packlist.packAt(currentPackIndex) |
||||
property int currentPackIndex: 0 |
||||
readonly property int stickerDim: 128 |
||||
readonly property int stickerDimPad: 128 + Nheko.paddingSmall |
||||
|
||||
title: qsTr("Image pack settings") |
||||
x: MainWindow.x + (MainWindow.width / 2) - (width / 2) |
||||
y: MainWindow.y + (MainWindow.height / 2) - (height / 2) |
||||
height: 400 |
||||
width: 600 |
||||
palette: Nheko.colors |
||||
color: Nheko.colors.base |
||||
modality: Qt.NonModal |
||||
flags: Qt.Dialog |
||||
|
||||
AdaptiveLayout { |
||||
id: adaptiveView |
||||
|
||||
anchors.fill: parent |
||||
singlePageMode: false |
||||
pageIndex: 0 |
||||
|
||||
AdaptiveLayoutElement { |
||||
id: packlistC |
||||
|
||||
visible: Settings.groupView |
||||
minimumWidth: 200 |
||||
collapsedWidth: 200 |
||||
preferredWidth: 300 |
||||
maximumWidth: 300 |
||||
|
||||
ListView { |
||||
model: packlist |
||||
clip: true |
||||
|
||||
ScrollHelper { |
||||
flickable: parent |
||||
anchors.fill: parent |
||||
enabled: !Settings.mobileMode |
||||
} |
||||
|
||||
delegate: Rectangle { |
||||
id: packItem |
||||
|
||||
property color background: Nheko.colors.window |
||||
property color importantText: Nheko.colors.text |
||||
property color unimportantText: Nheko.colors.buttonText |
||||
property color bubbleBackground: Nheko.colors.highlight |
||||
property color bubbleText: Nheko.colors.highlightedText |
||||
required property string displayName |
||||
required property string avatarUrl |
||||
required property bool fromAccountData |
||||
required property bool fromCurrentRoom |
||||
required property int index |
||||
|
||||
color: background |
||||
height: avatarSize + 2 * Nheko.paddingMedium |
||||
width: ListView.view.width |
||||
state: "normal" |
||||
states: [ |
||||
State { |
||||
name: "highlight" |
||||
when: hovered.hovered && !(index == currentPackIndex) |
||||
|
||||
PropertyChanges { |
||||
target: packItem |
||||
background: Nheko.colors.dark |
||||
importantText: Nheko.colors.brightText |
||||
unimportantText: Nheko.colors.brightText |
||||
bubbleBackground: Nheko.colors.highlight |
||||
bubbleText: Nheko.colors.highlightedText |
||||
} |
||||
|
||||
}, |
||||
State { |
||||
name: "selected" |
||||
when: index == currentPackIndex |
||||
|
||||
PropertyChanges { |
||||
target: packItem |
||||
background: Nheko.colors.highlight |
||||
importantText: Nheko.colors.highlightedText |
||||
unimportantText: Nheko.colors.highlightedText |
||||
bubbleBackground: Nheko.colors.highlightedText |
||||
bubbleText: Nheko.colors.highlight |
||||
} |
||||
|
||||
} |
||||
] |
||||
|
||||
TapHandler { |
||||
margin: -Nheko.paddingSmall |
||||
onSingleTapped: currentPackIndex = index |
||||
} |
||||
|
||||
HoverHandler { |
||||
id: hovered |
||||
} |
||||
|
||||
RowLayout { |
||||
spacing: Nheko.paddingMedium |
||||
anchors.fill: parent |
||||
anchors.margins: Nheko.paddingMedium |
||||
|
||||
Avatar { |
||||
// In the future we could show an online indicator by setting the userid for the avatar |
||||
//userid: Nheko.currentUser.userid |
||||
|
||||
id: avatar |
||||
|
||||
enabled: false |
||||
Layout.alignment: Qt.AlignVCenter |
||||
height: avatarSize |
||||
width: avatarSize |
||||
url: avatarUrl.replace("mxc://", "image://MxcImage/") |
||||
displayName: packItem.displayName |
||||
} |
||||
|
||||
ColumnLayout { |
||||
id: textContent |
||||
|
||||
Layout.alignment: Qt.AlignLeft |
||||
Layout.fillWidth: true |
||||
Layout.minimumWidth: 100 |
||||
width: parent.width - avatar.width |
||||
Layout.preferredWidth: parent.width - avatar.width |
||||
spacing: Nheko.paddingSmall |
||||
|
||||
RowLayout { |
||||
Layout.fillWidth: true |
||||
spacing: 0 |
||||
|
||||
ElidedLabel { |
||||
Layout.alignment: Qt.AlignBottom |
||||
color: packItem.importantText |
||||
elideWidth: textContent.width - Nheko.paddingMedium |
||||
fullText: displayName |
||||
textFormat: Text.PlainText |
||||
} |
||||
|
||||
Item { |
||||
Layout.fillWidth: true |
||||
} |
||||
|
||||
} |
||||
|
||||
RowLayout { |
||||
Layout.fillWidth: true |
||||
spacing: 0 |
||||
|
||||
ElidedLabel { |
||||
color: packItem.unimportantText |
||||
font.pixelSize: fontMetrics.font.pixelSize * 0.9 |
||||
elideWidth: textContent.width - Nheko.paddingSmall |
||||
fullText: { |
||||
if (fromAccountData) |
||||
return qsTr("Private pack"); |
||||
else if (fromCurrentRoom) |
||||
return qsTr("Pack from this room"); |
||||
else |
||||
return qsTr("Globally enabled pack"); |
||||
} |
||||
textFormat: Text.PlainText |
||||
} |
||||
|
||||
Item { |
||||
Layout.fillWidth: true |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
AdaptiveLayoutElement { |
||||
id: packinfoC |
||||
|
||||
Rectangle { |
||||
color: Nheko.colors.window |
||||
|
||||
ColumnLayout { |
||||
id: packinfo |
||||
|
||||
property string packName: currentPack ? currentPack.packname : "" |
||||
property string avatarUrl: currentPack ? currentPack.avatarUrl : "" |
||||
|
||||
anchors.fill: parent |
||||
anchors.margins: Nheko.paddingLarge |
||||
spacing: Nheko.paddingLarge |
||||
|
||||
Avatar { |
||||
url: packinfo.avatarUrl.replace("mxc://", "image://MxcImage/") |
||||
displayName: packinfo.packName |
||||
height: 100 |
||||
width: 100 |
||||
Layout.alignment: Qt.AlignHCenter |
||||
enabled: false |
||||
} |
||||
|
||||
MatrixText { |
||||
text: packinfo.packName |
||||
font.pixelSize: 24 |
||||
Layout.alignment: Qt.AlignHCenter |
||||
} |
||||
|
||||
GridLayout { |
||||
Layout.alignment: Qt.AlignHCenter |
||||
visible: currentPack && currentPack.roomid != "" |
||||
columns: 2 |
||||
rowSpacing: Nheko.paddingMedium |
||||
|
||||
MatrixText { |
||||
text: qsTr("Enable globally") |
||||
} |
||||
|
||||
ToggleButton { |
||||
ToolTip.text: qsTr("Enables this pack to be used in all rooms") |
||||
checked: currentPack ? currentPack.isGloballyEnabled : false |
||||
onClicked: currentPack.isGloballyEnabled = !currentPack.isGloballyEnabled |
||||
Layout.alignment: Qt.AlignRight |
||||
} |
||||
|
||||
} |
||||
|
||||
GridView { |
||||
Layout.fillHeight: true |
||||
Layout.fillWidth: true |
||||
model: currentPack |
||||
cellWidth: stickerDimPad |
||||
cellHeight: stickerDimPad |
||||
boundsBehavior: Flickable.StopAtBounds |
||||
clip: true |
||||
currentIndex: -1 // prevent sorting from stealing focus |
||||
cacheBuffer: 500 |
||||
|
||||
ScrollHelper { |
||||
flickable: parent |
||||
anchors.fill: parent |
||||
enabled: !Settings.mobileMode |
||||
} |
||||
|
||||
// Individual emoji |
||||
delegate: AbstractButton { |
||||
width: stickerDim |
||||
height: stickerDim |
||||
hoverEnabled: true |
||||
ToolTip.text: ":" + model.shortcode + ": - " + model.body |
||||
ToolTip.visible: hovered |
||||
|
||||
contentItem: Image { |
||||
height: stickerDim |
||||
width: stickerDim |
||||
source: model.url.replace("mxc://", "image://MxcImage/") |
||||
fillMode: Image.PreserveAspectFit |
||||
} |
||||
|
||||
background: Rectangle { |
||||
anchors.fill: parent |
||||
color: hovered ? Nheko.colors.highlight : 'transparent' |
||||
radius: 5 |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
footer: DialogButtonBox { |
||||
id: buttons |
||||
|
||||
Button { |
||||
text: qsTr("Close") |
||||
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole |
||||
onClicked: win.close() |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,76 @@ |
||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "ImagePackListModel.h" |
||||
|
||||
#include <QQmlEngine> |
||||
|
||||
#include "Cache_p.h" |
||||
#include "SingleImagePackModel.h" |
||||
|
||||
ImagePackListModel::ImagePackListModel(const std::string &roomId, QObject *parent) |
||||
: QAbstractListModel(parent) |
||||
, room_id(roomId) |
||||
{ |
||||
auto packs_ = cache::client()->getImagePacks(room_id, std::nullopt); |
||||
|
||||
for (const auto &pack : packs_) { |
||||
packs.push_back( |
||||
QSharedPointer<SingleImagePackModel>(new SingleImagePackModel(pack))); |
||||
} |
||||
} |
||||
|
||||
int |
||||
ImagePackListModel::rowCount(const QModelIndex &) const |
||||
{ |
||||
return (int)packs.size(); |
||||
} |
||||
|
||||
QHash<int, QByteArray> |
||||
ImagePackListModel::roleNames() const |
||||
{ |
||||
return { |
||||
{Roles::DisplayName, "displayName"}, |
||||
{Roles::AvatarUrl, "avatarUrl"}, |
||||
{Roles::FromAccountData, "fromAccountData"}, |
||||
{Roles::FromCurrentRoom, "fromCurrentRoom"}, |
||||
{Roles::StateKey, "statekey"}, |
||||
{Roles::RoomId, "roomid"}, |
||||
}; |
||||
} |
||||
|
||||
QVariant |
||||
ImagePackListModel::data(const QModelIndex &index, int role) const |
||||
{ |
||||
if (hasIndex(index.row(), index.column(), index.parent())) { |
||||
const auto &pack = packs.at(index.row()); |
||||
switch (role) { |
||||
case Roles::DisplayName: |
||||
return pack->packname(); |
||||
case Roles::AvatarUrl: |
||||
return pack->avatarUrl(); |
||||
case Roles::FromAccountData: |
||||
return pack->roomid().isEmpty(); |
||||
case Roles::FromCurrentRoom: |
||||
return pack->roomid().toStdString() == this->room_id; |
||||
case Roles::StateKey: |
||||
return pack->statekey(); |
||||
case Roles::RoomId: |
||||
return pack->roomid(); |
||||
default: |
||||
return {}; |
||||
} |
||||
} |
||||
return {}; |
||||
} |
||||
|
||||
SingleImagePackModel * |
||||
ImagePackListModel::packAt(int row) |
||||
{ |
||||
if (row < 0 || static_cast<size_t>(row) >= packs.size()) |
||||
return {}; |
||||
auto e = packs.at(row).get(); |
||||
QQmlEngine::setObjectOwnership(e, QQmlEngine::CppOwnership); |
||||
return e; |
||||
} |
@ -0,0 +1,37 @@ |
||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once |
||||
|
||||
#include <QAbstractListModel> |
||||
#include <QQmlEngine> |
||||
#include <QSharedPointer> |
||||
|
||||
class SingleImagePackModel; |
||||
class ImagePackListModel : public QAbstractListModel |
||||
{ |
||||
Q_OBJECT |
||||
public: |
||||
enum Roles |
||||
{ |
||||
DisplayName = Qt::UserRole, |
||||
AvatarUrl, |
||||
FromAccountData, |
||||
FromCurrentRoom, |
||||
StateKey, |
||||
RoomId, |
||||
}; |
||||
|
||||
ImagePackListModel(const std::string &roomId, 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; |
||||
|
||||
Q_INVOKABLE SingleImagePackModel *packAt(int row); |
||||
|
||||
private: |
||||
std::string room_id; |
||||
|
||||
std::vector<QSharedPointer<SingleImagePackModel>> packs; |
||||
}; |
@ -0,0 +1,100 @@ |
||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "SingleImagePackModel.h" |
||||
|
||||
#include "Cache_p.h" |
||||
#include "MatrixClient.h" |
||||
|
||||
SingleImagePackModel::SingleImagePackModel(ImagePackInfo pack_, QObject *parent) |
||||
: QAbstractListModel(parent) |
||||
, roomid_(std::move(pack_.source_room)) |
||||
, statekey_(std::move(pack_.state_key)) |
||||
, pack(std::move(pack_.pack)) |
||||
{ |
||||
if (!pack.pack) |
||||
pack.pack = mtx::events::msc2545::ImagePack::PackDescription{}; |
||||
|
||||
for (const auto &e : pack.images) |
||||
shortcodes.push_back(e.first); |
||||
} |
||||
|
||||
int |
||||
SingleImagePackModel::rowCount(const QModelIndex &) const |
||||
{ |
||||
return (int)shortcodes.size(); |
||||
} |
||||
|
||||
QHash<int, QByteArray> |
||||
SingleImagePackModel::roleNames() const |
||||
{ |
||||
return { |
||||
{Roles::Url, "url"}, |
||||
{Roles::ShortCode, "shortCode"}, |
||||
{Roles::Body, "body"}, |
||||
{Roles::IsEmote, "isEmote"}, |
||||
{Roles::IsSticker, "isSticker"}, |
||||
}; |
||||
} |
||||
|
||||
QVariant |
||||
SingleImagePackModel::data(const QModelIndex &index, int role) const |
||||
{ |
||||
if (hasIndex(index.row(), index.column(), index.parent())) { |
||||
const auto &img = pack.images.at(shortcodes.at(index.row())); |
||||
switch (role) { |
||||
case Url: |
||||
return QString::fromStdString(img.url); |
||||
case ShortCode: |
||||
return QString::fromStdString(shortcodes.at(index.row())); |
||||
case Body: |
||||
return QString::fromStdString(img.body); |
||||
case IsEmote: |
||||
return img.overrides_usage() ? img.is_emoji() : pack.pack->is_emoji(); |
||||
case IsSticker: |
||||
return img.overrides_usage() ? img.is_sticker() : pack.pack->is_sticker(); |
||||
default: |
||||
return {}; |
||||
} |
||||
} |
||||
return {}; |
||||
} |
||||
|
||||
bool |
||||
SingleImagePackModel::isGloballyEnabled() const |
||||
{ |
||||
if (auto roomPacks = |
||||
cache::client()->getAccountData(mtx::events::EventType::ImagePackRooms)) { |
||||
if (auto tmp = std::get_if< |
||||
mtx::events::EphemeralEvent<mtx::events::msc2545::ImagePackRooms>>( |
||||
&*roomPacks)) { |
||||
if (tmp->content.rooms.count(roomid_) && |
||||
tmp->content.rooms.at(roomid_).count(statekey_)) |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
void |
||||
SingleImagePackModel::setGloballyEnabled(bool enabled) |
||||
{ |
||||
mtx::events::msc2545::ImagePackRooms content{}; |
||||
if (auto roomPacks = |
||||
cache::client()->getAccountData(mtx::events::EventType::ImagePackRooms)) { |
||||
if (auto tmp = std::get_if< |
||||
mtx::events::EphemeralEvent<mtx::events::msc2545::ImagePackRooms>>( |
||||
&*roomPacks)) { |
||||
content = tmp->content; |
||||
} |
||||
} |
||||
|
||||
if (enabled) |
||||
content.rooms[roomid_][statekey_] = {}; |
||||
else |
||||
content.rooms[roomid_].erase(statekey_); |
||||
|
||||
http::client()->put_account_data(content, [this](mtx::http::RequestErr) { |
||||
// emit this->globallyEnabledChanged();
|
||||
}); |
||||
} |
@ -0,0 +1,61 @@ |
||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once |
||||
|
||||
#include <QAbstractListModel> |
||||
|
||||
#include <mtx/events/mscs/image_packs.hpp> |
||||
|
||||
#include "CacheStructs.h" |
||||
|
||||
class SingleImagePackModel : public QAbstractListModel |
||||
{ |
||||
Q_OBJECT |
||||
|
||||
Q_PROPERTY(QString roomid READ roomid CONSTANT) |
||||
Q_PROPERTY(QString statekey READ statekey CONSTANT) |
||||
Q_PROPERTY(QString attribution READ statekey CONSTANT) |
||||
Q_PROPERTY(QString packname READ packname CONSTANT) |
||||
Q_PROPERTY(QString avatarUrl READ avatarUrl CONSTANT) |
||||
Q_PROPERTY(bool isStickerPack READ isStickerPack CONSTANT) |
||||
Q_PROPERTY(bool isEmotePack READ isEmotePack CONSTANT) |
||||
Q_PROPERTY(bool isGloballyEnabled READ isGloballyEnabled WRITE setGloballyEnabled NOTIFY |
||||
globallyEnabledChanged) |
||||
public: |
||||
enum Roles |
||||
{ |
||||
Url = Qt::UserRole, |
||||
ShortCode, |
||||
Body, |
||||
IsEmote, |
||||
IsSticker, |
||||
}; |
||||
|
||||
SingleImagePackModel(ImagePackInfo pack_, 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; |
||||
|
||||
QString roomid() const { return QString::fromStdString(roomid_); } |
||||
QString statekey() const { return QString::fromStdString(statekey_); } |
||||
QString packname() const { return QString::fromStdString(pack.pack->display_name); } |
||||
QString attribution() const { return QString::fromStdString(pack.pack->attribution); } |
||||
QString avatarUrl() const { return QString::fromStdString(pack.pack->avatar_url); } |
||||
bool isStickerPack() const { return pack.pack->is_sticker(); } |
||||
bool isEmotePack() const { return pack.pack->is_emoji(); } |
||||
|
||||
bool isGloballyEnabled() const; |
||||
void setGloballyEnabled(bool enabled); |
||||
|
||||
signals: |
||||
void globallyEnabledChanged(); |
||||
|
||||
private: |
||||
std::string roomid_; |
||||
std::string statekey_; |
||||
|
||||
mtx::events::msc2545::ImagePack pack; |
||||
std::vector<std::string> shortcodes; |
||||
}; |
Loading…
Reference in new issue