Cancel member actions when a click becomes a drag
This commit is contained in:
1 parent
1820128f5b
commit
a7a7e4b5a1
3 files changed
+58
-6
No files matched your search
@@ -158,6 +158,16 @@ void AccountDelegate::paint(QPainter *painter, const QStyleOptionViewItem &optio
|
||||
bool AccountDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option,
|
||||
const QModelIndex& index) {
|
||||
Q_UNUSED(model);
|
||||
if (event->type() == QEvent::MouseMove) {
|
||||
const auto* mouse = static_cast<QMouseEvent*>(event);
|
||||
if (m_pressedIndex.isValid() && (!(mouse->buttons() & Qt::LeftButton) ||
|
||||
(mouse->pos() - m_pressedPosition).manhattanLength() >= ClickDistance)) {
|
||||
GUI_DEBUG("AccountDelegate: action cancelled row=%d action=%d distance=%d left_down=%d", m_pressedIndex.row(),
|
||||
int(m_pressedAction), (mouse->pos() - m_pressedPosition).manhattanLength(), bool(mouse->buttons() & Qt::LeftButton));
|
||||
m_pressedIndex = QPersistentModelIndex(); m_pressedAction = Action::None;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (event->type() != QEvent::MouseButtonPress && event->type() != QEvent::MouseButtonRelease &&
|
||||
event->type() != QEvent::MouseButtonDblClick) return false;
|
||||
auto* mouse = static_cast<QMouseEvent*>(event);
|
||||
@@ -172,9 +182,10 @@ bool AccountDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, cons
|
||||
if (event->type() == QEvent::MouseButtonDblClick) return action != Action::None;
|
||||
const bool handled = action != Action::None || m_pressedIndex.isValid();
|
||||
const bool activate = enabled && action != Action::None && action == m_pressedAction && m_pressedIndex == index &&
|
||||
(mouse->pos() - m_pressedPosition).manhattanLength() < 5;
|
||||
(mouse->pos() - m_pressedPosition).manhattanLength() < ClickDistance;
|
||||
m_pressedIndex = QPersistentModelIndex(); m_pressedAction = Action::None;
|
||||
if (activate) {
|
||||
GUI_DEBUG("AccountDelegate: action activated row=%d action=%d", index.row(), int(action));
|
||||
if (action == Action::Avatar) emit avatarRequested(index);
|
||||
else if (action == Action::RadioMute) emit radioMuteRequested(index);
|
||||
else if (action == Action::RadioCall) emit radioCallRequested(index);
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
/* Строка участника: аватар, имя и три действия — звук рации, вызов рации, обычный звонок.
|
||||
* Вызывает signals только после клика без перетаскивания; вызовы доступны online-пирам. */
|
||||
* GUI-поток, модель принадлежит view. DisplayRole — имя, DecorationRole — аватар,
|
||||
* UserRole+1 — подпись, +2 — self, +3 — CHAT_MEMBER_FLAG_*, +4 — storage;
|
||||
* MemberListModel::OnlineRole/RadioSpeakingRole определяют доступность и индикацию.
|
||||
* Аватар доступен всем, mute — чужим участникам независимо от online, вызовы — online-пирам.
|
||||
* Собственная строка показывает состояние рации без кнопок mute/вызова.
|
||||
* Сигнал только при LeftButton release в том же действии/строке после press:
|
||||
* MouseMove со смещением >=5 по Manhattan или без LeftButton отменяет жест, включая возврат курсора назад. */
|
||||
#pragma once
|
||||
|
||||
#include <QStyledItemDelegate>
|
||||
@@ -8,26 +14,28 @@
|
||||
class AccountDelegate : public QStyledItemDelegate {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AccountDelegate(QObject *parent = nullptr);
|
||||
explicit AccountDelegate(QObject *parent = nullptr); // parent владеет по правилам QObject
|
||||
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const override;
|
||||
QSize sizeHint(const QStyleOptionViewItem &option,
|
||||
QSize sizeHint(const QStyleOptionViewItem &option, // 160 × MemberListModel::kRowHeight, независимо от данных
|
||||
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:
|
||||
// index заимствует модель; для отложенной работы получатель сохраняет node ID, не QModelIndex через reset.
|
||||
void radioCallRequested(const QModelIndex& index);
|
||||
void callRequested(const QModelIndex& index);
|
||||
void avatarRequested(const QModelIndex& index);
|
||||
void radioMuteRequested(const QModelIndex& index);
|
||||
private:
|
||||
static constexpr int ClickDistance = 5;
|
||||
enum class Action { None, Avatar, RadioMute, RadioCall, Call };
|
||||
static QRect actionRect(const QRect& row, Action action);
|
||||
static Action actionAt(const QPoint& position, const QRect& row, bool self);
|
||||
QPersistentModelIndex m_pressedIndex; /* строка, на которой начался клик */
|
||||
QPoint m_pressedPosition;
|
||||
Action m_pressedAction = Action::None;
|
||||
QPoint m_pressedPosition; // координаты viewport при press
|
||||
Action m_pressedAction = Action::None; // ожидаемое действие; сбрасывается при release/отмене жеста
|
||||
};
|
||||
@@ -483,6 +483,39 @@ private slots:
|
||||
QCOMPARE(pixels(known | busy, true, QColor("#4CAF50")), 0);
|
||||
}
|
||||
|
||||
void draggingAwayAndBackDoesNotCallMember_data() {
|
||||
QTest::addColumn<int>("action");
|
||||
QTest::newRow("avatar") << 0;
|
||||
QTest::newRow("radio-call") << 1;
|
||||
QTest::newRow("call") << 2;
|
||||
QTest::newRow("radio-mute") << 3;
|
||||
}
|
||||
|
||||
void draggingAwayAndBackDoesNotCallMember() {
|
||||
QFETCH(int, action);
|
||||
QScopedValueRollback<debug_level_t> diagnostic(g_debug_config.category_levels[DEBUG_CATEGORY_GENERAL], DEBUG_LEVEL_DEBUG);
|
||||
QStandardItemModel model;
|
||||
auto* peer = new QStandardItem("peer"); model.appendRow(peer);
|
||||
peer->setData(true, MemberListModel::OnlineRole);
|
||||
QListView view; AccountDelegate delegate;
|
||||
view.setModel(&model); view.setItemDelegate(&delegate); view.resize(280, 140); view.show();
|
||||
QTest::qWait(30);
|
||||
const auto signal = action == 0 ? &AccountDelegate::avatarRequested : action == 1 ? &AccountDelegate::radioCallRequested :
|
||||
action == 2 ? &AccountDelegate::callRequested : &AccountDelegate::radioMuteRequested;
|
||||
QSignalSpy activated(&delegate, signal);
|
||||
const QRect row = view.visualRect(model.index(0, 0));
|
||||
const QPoint radio(action == 0 ? row.left() + 21 : row.right() - (action == 1 ? 50 : action == 2 ? 17 : 82), row.center().y());
|
||||
QTest::mousePress(view.viewport(), Qt::LeftButton, Qt::NoModifier, radio);
|
||||
for (QPoint point : {radio + QPoint(-15, 0), radio}) {
|
||||
QMouseEvent move(QEvent::MouseMove, point, view.viewport()->mapToGlobal(point), Qt::NoButton, Qt::LeftButton, Qt::NoModifier);
|
||||
QApplication::sendEvent(view.viewport(), &move);
|
||||
}
|
||||
QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::NoModifier, radio);
|
||||
QCOMPARE(activated.count(), 0);
|
||||
QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, radio);
|
||||
QCOMPARE(activated.count(), 1);
|
||||
}
|
||||
|
||||
void radioMuteButtonTargetsMemberAndChangesColor() {
|
||||
QStandardItemModel model;
|
||||
auto* other = new QStandardItem("other"); other->setData(false, Qt::UserRole + 2); model.appendRow(other);
|
||||
|
||||
Reference in new issue
Block a user