Add local per-member radio mute controls on desktop and Android
This commit is contained in:
1 parent
a069d11ef9
commit
7e19cbaf3a
33 files changed
+735
-27
No files matched your search
@@ -5,11 +5,39 @@
|
||||
#include <QPainter>
|
||||
#include <QFont>
|
||||
#include <QMouseEvent>
|
||||
#include <QHelpEvent>
|
||||
#include <QToolTip>
|
||||
#include <QPainterPath>
|
||||
#include <QCoreApplication>
|
||||
#include <QAbstractItemView>
|
||||
|
||||
extern "C" {
|
||||
#include "chat/chat_member.h"
|
||||
}
|
||||
|
||||
static QRect inviteButtonRect(const QRect& row) {
|
||||
return QRect(row.right() - 31, row.center().y() - 14, 28, 28);
|
||||
}
|
||||
|
||||
static QRect radioMuteButtonRect(const QRect& row) {
|
||||
return QRect(row.right() - 63, row.center().y() - 14, 28, 28);
|
||||
}
|
||||
|
||||
static void drawRadioMuteButton(QPainter* painter, const QRect& button, bool muted) {
|
||||
painter->save();
|
||||
painter->setRenderHint(QPainter::Antialiasing);
|
||||
painter->translate(button.center());
|
||||
QColor color(muted ? "#EF5350" : "#4CAF50");
|
||||
QPainterPath speaker;
|
||||
speaker.moveTo(-8, -3); speaker.lineTo(-4, -3); speaker.lineTo(1, -7);
|
||||
speaker.lineTo(1, 7); speaker.lineTo(-4, 3); speaker.lineTo(-8, 3); speaker.closeSubpath();
|
||||
painter->fillPath(speaker, color);
|
||||
painter->setPen(QPen(color, 1.8, Qt::SolidLine, Qt::RoundCap)); painter->setBrush(Qt::NoBrush);
|
||||
painter->drawArc(QRectF(-3, -5, 10, 10), -60 * 16, 120 * 16);
|
||||
painter->drawArc(QRectF(-6, -8, 16, 16), -60 * 16, 120 * 16);
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
static QRect avatarRect(const QRect& row) {
|
||||
const int size = MemberListModel::kAvatarSize;
|
||||
return QRect(row.left() + 4, row.top() + (row.height() - size) / 2, size, size);
|
||||
@@ -41,7 +69,7 @@ void AccountDelegate::paint(QPainter *painter, const QStyleOptionViewItem &optio
|
||||
|
||||
const int x = option.rect.left() + iconSz + margin * 3;
|
||||
const bool canInvite = !index.data(Qt::UserRole + 2).toBool();
|
||||
const int w = option.rect.right() - x - margin - (canInvite ? 32 : 0);
|
||||
const int w = option.rect.right() - x - margin - (canInvite ? 64 : 0);
|
||||
const int yMid = option.rect.top() + option.rect.height() / 2;
|
||||
|
||||
uint8_t flags = (uint8_t)(index.data(Qt::UserRole + 3).toInt() & 0xFF);
|
||||
@@ -90,6 +118,7 @@ void AccountDelegate::paint(QPainter *painter, const QStyleOptionViewItem &optio
|
||||
Qt::AlignLeft | Qt::AlignTop, sub);
|
||||
|
||||
if (canInvite) {
|
||||
drawRadioMuteButton(painter, radioMuteButtonRect(option.rect), flags & CHAT_MEMBER_FLAG_RADIO_MUTED);
|
||||
QFont plusFont = nameFont; plusFont.setPixelSize(22); plusFont.setStrikeOut(false);
|
||||
painter->setFont(plusFont);
|
||||
painter->setPen(option.palette.color(QPalette::Link));
|
||||
@@ -105,6 +134,18 @@ bool AccountDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, cons
|
||||
event->type() != QEvent::MouseButtonDblClick) return false;
|
||||
auto* mouse = static_cast<QMouseEvent*>(event);
|
||||
if (mouse->button() != Qt::LeftButton) return false;
|
||||
const bool onMute = !index.data(Qt::UserRole + 2).toBool() && radioMuteButtonRect(option.rect).contains(mouse->pos());
|
||||
if (event->type() == QEvent::MouseButtonPress) {
|
||||
m_radioMutePress = onMute ? index : QModelIndex(); m_radioMutePosition = mouse->pos();
|
||||
}
|
||||
if (onMute || m_radioMutePress.isValid()) {
|
||||
if (event->type() == QEvent::MouseButtonRelease) {
|
||||
const bool activate = onMute && m_radioMutePress == index && (mouse->pos() - m_radioMutePosition).manhattanLength() < 5;
|
||||
m_radioMutePress = QPersistentModelIndex();
|
||||
if (activate) emit radioMuteRequested(index);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
const bool onAvatar = avatarRect(option.rect).contains(mouse->pos());
|
||||
if (event->type()==QEvent::MouseButtonPress) {
|
||||
m_avatarPress=onAvatar ? index : QModelIndex(); m_avatarPosition=mouse->pos();
|
||||
@@ -125,6 +166,17 @@ bool AccountDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, cons
|
||||
return activate;
|
||||
}
|
||||
|
||||
bool AccountDelegate::helpEvent(QHelpEvent* event, QAbstractItemView* view, const QStyleOptionViewItem& option,
|
||||
const QModelIndex& index) {
|
||||
if (event && !index.data(Qt::UserRole + 2).toBool() && radioMuteButtonRect(option.rect).contains(event->pos())) {
|
||||
const bool muted = index.data(Qt::UserRole + 3).toInt() & CHAT_MEMBER_FLAG_RADIO_MUTED;
|
||||
QToolTip::showText(event->globalPos(), QCoreApplication::translate("ChatGui",
|
||||
muted ? "Listen to this member in radio" : "Mute this member in radio"), view);
|
||||
return true;
|
||||
}
|
||||
return QStyledItemDelegate::helpEvent(event, view, option, index);
|
||||
}
|
||||
|
||||
QSize AccountDelegate::sizeHint(const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const {
|
||||
(void)option; (void)index;
|
||||
|
||||
@@ -15,11 +15,16 @@ public:
|
||||
const QModelIndex &index) const override;
|
||||
bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option,
|
||||
const QModelIndex& index) override;
|
||||
bool helpEvent(QHelpEvent* event, QAbstractItemView* view, const QStyleOptionViewItem& option,
|
||||
const QModelIndex& index) override;
|
||||
signals:
|
||||
void inviteRequested(const QModelIndex& index);
|
||||
void avatarRequested(const QModelIndex& index);
|
||||
void radioMuteRequested(const QModelIndex& index);
|
||||
private:
|
||||
QPersistentModelIndex m_invitePress;
|
||||
QPersistentModelIndex m_avatarPress;
|
||||
QPoint m_avatarPosition;
|
||||
QPersistentModelIndex m_radioMutePress;
|
||||
QPoint m_radioMutePosition;
|
||||
};
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <QDateTime>
|
||||
#include <QFont>
|
||||
#include <QSet>
|
||||
#include <QMessageBox>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include "../../../lib/platform_compat.h"
|
||||
@@ -85,6 +86,11 @@ AccountList::AccountList(DbManager* db, QWidget *parent)
|
||||
m_listView->setModel(m_model);
|
||||
auto* delegate = new AccountDelegate(m_listView);
|
||||
m_listView->setItemDelegate(delegate);
|
||||
connect(delegate, &AccountDelegate::radioMuteRequested, this, [this](const QModelIndex& index) {
|
||||
if (gui_bridge_toggle_member_radio_mute(m_channelId.toUtf8().constData(), m_model->nodeIdAt(index.row())) < 0)
|
||||
QMessageBox::warning(this, QCoreApplication::translate("ChatGui", "Error"),
|
||||
QCoreApplication::translate("ChatGui", "Cannot change radio listening setting"));
|
||||
});
|
||||
connect(delegate, &AccountDelegate::avatarRequested, this, [this](const QModelIndex& index) {
|
||||
showAvatarGallery(m_channelId, m_model->nodeIdAt(index.row()));
|
||||
});
|
||||
@@ -143,6 +149,11 @@ AccountList::AccountList(DbManager* db, QWidget *parent)
|
||||
});
|
||||
|
||||
g_detailTarget = this;
|
||||
gui_bridge_set_radio_mute_result_cb([](const uint8_t* data, int len) {
|
||||
if (len != 17) { DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "radio: invalid GUI mute result bytes=%d", len); return; }
|
||||
if (g_detailTarget && !data[16]) QMessageBox::warning(g_detailTarget, QCoreApplication::translate("ChatGui", "Error"),
|
||||
QCoreApplication::translate("ChatGui", "Cannot change radio listening setting"));
|
||||
});
|
||||
gui_bridge_set_member_detail_cb(memberDetailCb);
|
||||
|
||||
static bool nodeinfoRegistered = false;
|
||||
@@ -160,7 +171,7 @@ AccountList::AccountList(DbManager* db, QWidget *parent)
|
||||
}
|
||||
|
||||
AccountList::~AccountList() {
|
||||
if (g_detailTarget == this) g_detailTarget = nullptr;
|
||||
if (g_detailTarget == this) { g_detailTarget = nullptr; gui_bridge_set_radio_mute_result_cb(nullptr); }
|
||||
}
|
||||
|
||||
void AccountList::setChannel(const QString& channelId) {
|
||||
|
||||
Reference in new issue
Block a user