Auto scroll during drag if touching top or bottom areas of list widget

This commit is contained in:
WarmUpTill 2022-03-22 21:34:53 +01:00 committed by WarmUpTill
parent 6a8066795b
commit dbbcf04b8a
2 changed files with 87 additions and 35 deletions

View File

@ -5,12 +5,14 @@
#include <QScrollArea>
#include <QVBoxLayout>
#include <QLabel>
#include <thread>
class MacroSegmentList : public QScrollArea {
Q_OBJECT
public:
MacroSegmentList(QWidget *parent = nullptr);
virtual ~MacroSegmentList();
void SetHelpMsg(const QString &msg);
void SetHelpMsgVisible(bool visible);
void Insert(int idx, QWidget *widget);
@ -40,6 +42,8 @@ private:
int GetDragIndex(const QPoint &);
int GetDropIndex(const QPoint &);
int GetWidgetIdx(const QPoint &);
void CheckScroll();
void CheckDropLine(const QPoint &);
bool IsInListArea(const QPoint &);
QRect GetContentItemRectWithPadding(int idx);
void HideLastDropLine();
@ -47,6 +51,9 @@ private:
int _dragPosition = -1;
int _dropLineIdx = -1;
QPoint _dragCursorPos;
std::thread _autoScrollThread;
std::atomic_bool _autoScroll{false};
QVBoxLayout *_layout;
QVBoxLayout *_contentLayout;

View File

@ -33,6 +33,14 @@ MacroSegmentList::MacroSegmentList(QWidget *parent)
setAcceptDrops(true);
}
MacroSegmentList::~MacroSegmentList()
{
if (_autoScrollThread.joinable()) {
_autoScroll = false;
_autoScrollThread.join();
}
}
int MacroSegmentList::GetDragIndex(const QPoint &pos)
{
for (int idx = 0; idx < _contentLayout->count(); ++idx) {
@ -172,7 +180,12 @@ void MacroSegmentList::mouseMoveEvent(QMouseEvent *event)
drag->setMimeData(mimedata);
drag->setPixmap(img);
drag->setHotSpot(event->pos());
_autoScroll = true;
_autoScrollThread =
std::thread(&MacroSegmentList::CheckScroll, this);
drag->exec();
_autoScroll = false;
_autoScrollThread.join();
}
}
@ -249,7 +262,73 @@ void MacroSegmentList::dragMoveEvent(QDragMoveEvent *event)
if (!widgetIsInLayout(widget, _contentLayout)) {
return;
}
const QPoint pos(mapToGlobal(event->pos()));
_dragCursorPos = (mapToGlobal(event->pos()));
CheckDropLine(_dragCursorPos);
}
QRect MacroSegmentList::GetContentItemRectWithPadding(int idx)
{
auto item = _contentLayout->itemAt(idx);
if (!item) {
return {};
}
int scrollOffset = 0;
if (verticalScrollBar()) {
scrollOffset = verticalScrollBar()->value();
}
const QRect itemRect = item->geometry().marginsAdded(
_contentLayout->contentsMargins());
const QRect rect(
mapToGlobal(QPoint(itemRect.topLeft().x(),
itemRect.topLeft().y() -
_contentLayout->spacing() -
scrollOffset)),
QSize(itemRect.size().width(),
itemRect.size().height() + _contentLayout->spacing()));
return rect;
}
int MacroSegmentList::GetWidgetIdx(const QPoint &pos)
{
int idx = -1;
for (int i = 0; i < _contentLayout->count(); ++i) {
if (GetContentItemRectWithPadding(i).contains(pos)) {
idx = i;
break;
}
}
return idx;
}
void MacroSegmentList::CheckScroll()
{
while (_autoScroll) {
const int scrollTrigger = 15;
const int scrollAmount = 1;
const QRect rect(mapToGlobal(QPoint(0, 0)), size());
const QRect upperScrollTrigger(
QPoint(rect.topLeft().x(),
rect.topLeft().y() - scrollTrigger),
QSize(rect.width(), scrollTrigger * 2));
if (upperScrollTrigger.contains(_dragCursorPos)) {
verticalScrollBar()->setValue(
verticalScrollBar()->value() - scrollAmount);
}
const QRect lowerScrollTrigger(
QPoint(rect.bottomLeft().x(),
rect.bottomLeft().y() - scrollTrigger),
QSize(rect.width(), scrollTrigger * 2));
if (lowerScrollTrigger.contains(_dragCursorPos)) {
verticalScrollBar()->setValue(
verticalScrollBar()->value() + scrollAmount);
}
std::this_thread::sleep_for(std::chrono::microseconds(50));
}
}
void MacroSegmentList::CheckDropLine(const QPoint &pos)
{
int idx = GetWidgetIdx(pos);
if (idx == _dragPosition) {
return;
@ -292,40 +371,6 @@ void MacroSegmentList::dragMoveEvent(QDragMoveEvent *event)
}
}
QRect MacroSegmentList::GetContentItemRectWithPadding(int idx)
{
auto item = _contentLayout->itemAt(idx);
if (!item) {
return {};
}
int scrollOffset = 0;
if (verticalScrollBar()) {
scrollOffset = verticalScrollBar()->value();
}
const QRect itemRect = item->geometry().marginsAdded(
_contentLayout->contentsMargins());
const QRect rect(
mapToGlobal(QPoint(itemRect.topLeft().x(),
itemRect.topLeft().y() -
_contentLayout->spacing() -
scrollOffset)),
QSize(itemRect.size().width(),
itemRect.size().height() + _contentLayout->spacing()));
return rect;
}
int MacroSegmentList::GetWidgetIdx(const QPoint &pos)
{
int idx = -1;
for (int i = 0; i < _contentLayout->count(); ++i) {
if (GetContentItemRectWithPadding(i).contains(pos)) {
idx = i;
break;
}
}
return idx;
}
bool MacroSegmentList::IsInListArea(const QPoint &pos)
{
const QRect layoutRect(mapToGlobal(_layout->contentsRect().topLeft()),