Polish the drag and drop experience by backporting some upstream commits.

ubuntu/cosmic
Simon Quigley 6 years ago
parent 6a115f8094
commit e49123d636

1
debian/changelog vendored

@ -1,6 +1,7 @@
pcmanfm-qt (0.13.0-2ubuntu1) UNRELEASED; urgency=medium
* Update Vcs-* and Maintainer for maintaining in Lubuntu.
* Polish the drag and drop experience by backporting some upstream commits.
-- Simon Quigley <tsimonq2@ubuntu.com> Sat, 14 Jul 2018 18:08:44 -0500

@ -0,0 +1,293 @@
Description: Fix DND and drop indicator on desktop
(1) When items are dragged and dropped on any desktop position other than
that of a directory, they will stick to it (and to its following positions),
moving next sticky items if needed. When desktop items are dropped on
themselves or on a directory, the DND menu will be shown.
.
(2) The drop rectangle (when dragging desktop items on desktop folders) is
also fixed by drawing it explicitly.
Author: Tsu Jan <tsujan2000@gmail.com>
Origin: upstream
Bug: https://github.com/lxqt/pcmanfm-qt/issues/706, https://github.com/lxqt/pcmanfm-qt/issues/722
Applied-Upstream: commit:748a105
Last-Update: 2018-07-14
--- a/pcmanfm/desktopwindow.cpp
+++ b/pcmanfm/desktopwindow.cpp
@@ -91,6 +91,7 @@ DesktopWindow::DesktopWindow(int screenN
listView_->setMovement(QListView::Snap);
listView_->setResizeMode(QListView::Adjust);
listView_->setFlow(QListView::TopToBottom);
+ listView_->setDropIndicatorShown(false); // we draw the drop indicator ourself
// This is to workaround Qt bug 54384 which affects Qt >= 5.6
// https://bugreports.qt.io/browse/QTBUG-54384
@@ -126,7 +127,6 @@ DesktopWindow::DesktopWindow(int screenN
connect(proxyModel_, &Fm::ProxyFolderModel::layoutChanged, this, &DesktopWindow::onLayoutChanged);
connect(proxyModel_, &Fm::ProxyFolderModel::sortFilterChanged, this, &DesktopWindow::onModelSortFilterChanged);
connect(proxyModel_, &Fm::ProxyFolderModel::dataChanged, this, &DesktopWindow::onDataChanged);
- connect(listView_, &QListView::indexesMoved, this, &DesktopWindow::onIndexesMoved);
}
// remove frame
@@ -710,41 +710,6 @@ void DesktopWindow::onDataChanged(const
}
}
-void DesktopWindow::onIndexesMoved(const QModelIndexList& indexes) {
- auto delegate = static_cast<Fm::FolderItemDelegate*>(listView_->itemDelegateForColumn(0));
- auto itemSize = delegate->itemSize();
- // remember the custom position for the items
- for(const QModelIndex& index : indexes) {
- // Under some circumstances, Qt might emit indexMoved for
- // every single cells in the same row. (when QAbstractItemView::SelectItems is set)
- // So indexes list may contain several indixes for the same row.
- // Since we only care about rows, not individual cells,
- // let's handle column 0 of every row here.
- if(index.column() == 0) {
- auto file = proxyModel_->fileInfoFromIndex(index);
- QRect itemRect = listView_->rectForIndex(index);
- QPoint tl = itemRect.topLeft();
- QRect workArea = qApp->desktop()->availableGeometry(screenNum_);
- workArea.adjust(12, 12, -12, -12);
-
- // check if the position is occupied by another item
- auto existingItem = std::find_if(customItemPos_.cbegin(), customItemPos_.cend(), [tl](const std::pair<std::string, QPoint>& elem){
- return elem.second == tl;
- });
-
- if(existingItem == customItemPos_.cend() // don't put items on each other
- && tl.x() >= workArea.x() && tl.y() >= workArea.y()
- && tl.x() + itemSize.width() <= workArea.right() + 1 // for historical reasons (-> Qt doc)
- && tl.y() + itemSize.height() <= workArea.bottom() + 1) { // as above
- customItemPos_[file->name()] = tl;
- // qDebug() << "indexMoved:" << name << index << itemRect;
- }
- }
- }
- saveItemPositions();
- queueRelayout();
-}
-
void DesktopWindow::onFolderStartLoading() { // desktop may be reloaded
if(model_) {
disconnect(model_, &Fm::FolderModel::filesAdded, this, &DesktopWindow::onFilesAdded);
@@ -1247,6 +1212,11 @@ bool DesktopWindow::eventFilter(QObject*
}
}
break;
+ case QEvent::Paint:
+ // NOTE: The drop indicator isn't drawn/updated automatically, perhaps,
+ // because we paint desktop ourself. So, we draw it here.
+ paintDropIndicator();
+ break;
default:
break;
}
@@ -1254,6 +1224,34 @@ bool DesktopWindow::eventFilter(QObject*
return Fm::FolderView::eventFilter(watched, event);
}
+void DesktopWindow::childDragMoveEvent(QDragMoveEvent* e) {
+ // see DesktopWindow::eventFilter for an explanation
+ QRect oldDropRect = dropRect_;
+ dropRect_ = QRect();
+ QModelIndex index = listView_->indexAt(e->pos());
+ if(index.isValid() && index.model()) {
+ QVariant data = index.model()->data(index, Fm::FolderModel::Role::FileInfoRole);
+ auto info = data.value<std::shared_ptr<const Fm::FileInfo>>();
+ if(info && info->isDir()) {
+ dropRect_ = listView_->rectForIndex(index);
+ }
+ }
+ if(oldDropRect != dropRect_){
+ listView_->viewport()->update();
+ }
+}
+
+void DesktopWindow::paintDropIndicator()
+{
+ if(!dropRect_.isNull()) {
+ QPainter painter(listView_->viewport());
+ QStyleOption opt;
+ opt.init(listView_->viewport());
+ opt.rect = dropRect_;
+ style()->drawPrimitive(QStyle::PE_IndicatorItemViewItemDrop, &opt, &painter, listView_);
+ }
+}
+
void DesktopWindow::childDropEvent(QDropEvent* e) {
const QMimeData* mimeData = e->mimeData();
bool moveItem = false;
@@ -1264,45 +1262,59 @@ void DesktopWindow::childDropEvent(QDrop
QModelIndex dropIndex = listView_->indexAt(e->pos());
if(dropIndex.isValid()) { // drop on an item
QModelIndexList selected = selectedIndexes(); // the dragged items
- if(selected.contains(dropIndex)) { // drop on self, ignore
- moveItem = true;
+ if(!selected.contains(dropIndex)) { // not a drop on self
+ if(auto file = proxyModel_->fileInfoFromIndex(dropIndex)) {
+ if(!file->isDir()) { // drop on a non-directory file
+ moveItem = true;
+ }
+ }
}
}
- else { // drop on a blank area
+ else { // drop on a blank area (maybe, between other items)
moveItem = true;
}
}
}
if(moveItem) {
e->accept();
- }
- else {
+ // move selected items to the drop position, putting them successively
auto delegate = static_cast<Fm::FolderItemDelegate*>(listView_->itemDelegateForColumn(0));
auto grid = delegate->itemSize();
+ QRect workArea = qApp->desktop()->availableGeometry(screenNum_);
+ workArea.adjust(12, 12, -12, -12);
+ QPoint pos = mapFromGlobal(e->pos());
+ const QModelIndexList indexes = selectedIndexes();
+ for(const QModelIndex& indx : indexes) {
+ if(auto file = proxyModel_->fileInfoFromIndex(indx)) {
+ stickToPosition(QString::fromStdString(file->name()), pos, workArea, grid);
+ }
+ }
+ saveItemPositions();
+ queueRelayout();
+ }
+ else {
Fm::FolderView::childDropEvent(e);
+ // remove the drop indicator
+ dropRect_ = QRect();
+ listView_->viewport()->update();
// position dropped items successively, starting with the drop rectangle
if(mimeData->hasUrls()
&& (e->dropAction() == Qt::CopyAction
|| e->dropAction() == Qt::MoveAction
|| e->dropAction() == Qt::LinkAction)) {
- QList<QUrl> urlList = mimeData->urls();
- for(int i = 0; i < urlList.count(); ++i) {
- std::string name = urlList.at(i).fileName().toUtf8().constData();
- if(!name.empty()) { // respect the positions of existing files
- QString desktopDir = XdgDir::readDesktopDir() + QString(QLatin1String("/"));
- if(!QFile::exists(desktopDir + QString::fromStdString(name))) {
- QRect workArea = qApp->desktop()->availableGeometry(screenNum_);
- workArea.adjust(12, 12, -12, -12);
- QPoint pos = mapFromGlobal(e->pos());
- alignToGrid(pos, workArea.topLeft(), grid, listView_->spacing());
- if(i > 0)
- pos.setY(pos.y() + grid.height() + listView_->spacing());
- if(pos.y() + grid.height() > workArea.bottom() + 1) {
- pos.setX(pos.x() + grid.width() + listView_->spacing());
- pos.setY(workArea.top());
- }
- customItemPos_[name] = pos;
- }
+ auto delegate = static_cast<Fm::FolderItemDelegate*>(listView_->itemDelegateForColumn(0));
+ auto grid = delegate->itemSize();
+ QRect workArea = qApp->desktop()->availableGeometry(screenNum_);
+ workArea.adjust(12, 12, -12, -12);
+ const QString desktopDir = XdgDir::readDesktopDir() + QString(QLatin1String("/"));
+ QPoint pos = mapFromGlobal(e->pos());
+ const QList<QUrl> urlList = mimeData->urls();
+ for(const QUrl& url : urlList) {
+ QString name = url.fileName();
+ if(!name.isEmpty()
+ // don't stick to the position if there is an overwrite prompt
+ && !QFile::exists(desktopDir + name)) {
+ stickToPosition(name, pos, workArea, grid);
}
}
saveItemPositions();
@@ -1310,10 +1322,39 @@ void DesktopWindow::childDropEvent(QDrop
}
}
+// This function recursively repositions next sticky items if needed.
+void DesktopWindow::stickToPosition(const QString& file, QPoint& pos, const QRect& workArea, const QSize& grid) {
+ // normalize the position
+ alignToGrid(pos, workArea.topLeft(), grid, listView_->spacing());
+ if(pos.y() + grid.height() > workArea.bottom() + 1) {
+ pos.setX(pos.x() + grid.width() + listView_->spacing());
+ pos.setY(workArea.top());
+ }
+ // find if there is a sticky item at this position
+ QString otherFile;
+ auto oldItem = std::find_if(customItemPos_.cbegin(),
+ customItemPos_.cend(),
+ [pos](const std::pair<std::string, QPoint>& elem) {
+ return elem.second == pos;
+ });
+ if(oldItem != customItemPos_.cend()) {
+ otherFile = QString::fromStdString(oldItem->first);
+ }
+ // stick to the position
+ customItemPos_[file.toStdString()] = pos;
+ // find the next position
+ pos.setY(pos.y() + grid.height() + listView_->spacing());
+ // if there was another sticky item at the same position, move it to the next position
+ if(!otherFile.isEmpty() && otherFile != file) {
+ QPoint nextPos = pos;
+ stickToPosition(otherFile, nextPos, workArea, grid);
+ }
+}
+
void DesktopWindow::alignToGrid(QPoint& pos, const QPoint& topLeft, const QSize& grid, const int spacing) {
qreal w = qAbs((qreal)pos.x() - (qreal)topLeft.x())
/ (qreal)(grid.width() + spacing);
- qreal h = qAbs(pos.y() - (qreal)topLeft.y())
+ qreal h = qAbs((qreal)pos.y() - (qreal)topLeft.y())
/ (qreal)(grid.height() + spacing);
pos.setX(topLeft.x() + qRound(w) * (grid.width() + spacing));
pos.setY(topLeft.y() + qRound(h) * (grid.height() + spacing));
@@ -1324,7 +1365,7 @@ void DesktopWindow::closeEvent(QCloseEve
event->ignore();
}
-void DesktopWindow::paintEvent(QPaintEvent *event) {
+void DesktopWindow::paintEvent(QPaintEvent* event) {
paintBackground(event);
QWidget::paintEvent(event);
}
--- a/pcmanfm/desktopwindow.h
+++ b/pcmanfm/desktopwindow.h
@@ -98,9 +98,10 @@ protected:
virtual bool event(QEvent* event) override;
virtual bool eventFilter(QObject* watched, QEvent* event) override;
+ virtual void childDragMoveEvent(QDragMoveEvent* e) override;
virtual void childDropEvent(QDropEvent* e) override;
virtual void closeEvent(QCloseEvent* event) override;
- virtual void paintEvent(QPaintEvent *event) override;
+ virtual void paintEvent(QPaintEvent* event) override;
protected Q_SLOTS:
void onOpenDirRequested(const Fm::FilePath& path, int target);
@@ -112,7 +113,6 @@ protected Q_SLOTS:
void onRowsInserted(const QModelIndex& parent, int start, int end);
void onLayoutChanged();
void onModelSortFilterChanged();
- void onIndexesMoved(const QModelIndexList& indexes);
void onDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight);
void onFolderStartLoading();
void onFolderFinishLoading();
@@ -136,6 +136,8 @@ private:
void removeBottomGap();
void addDesktopActions(QMenu* menu);
void paintBackground(QPaintEvent* event);
+ void paintDropIndicator();
+ void stickToPosition(const QString& file, QPoint& pos, const QRect& workArea, const QSize& grid);
static void alignToGrid(QPoint& pos, const QPoint& topLeft, const QSize& grid, const int spacing);
private:
@@ -164,6 +166,8 @@ private:
QHash<QModelIndex, QString> displayNames_; // only for desktop entries and shortcuts
QTimer* relayoutTimer_;
QTimer* selectionTimer_;
+
+ QRect dropRect_;
};
}

@ -0,0 +1,89 @@
Description: Always drop into the cell behind cursor
Drop desktop items into the cell behind the cursor, regardless of the cursor
position inside the cell.
.
Previously, if the cursor was in the right half of the cell, the item would
be dropped into the next cell on the right, and a similar thing happened with
the right bottom half.
Author: Tsu Jan <tsujan2000@gmail.com>
Origin: upstream
Bug: https://github.com/lxqt/pcmanfm-qt/issues/728
Applied-Upstream: commit:a7898c9
Last-Update: 2018-07-14
--- a/pcmanfm/desktopwindow.cpp
+++ b/pcmanfm/desktopwindow.cpp
@@ -59,6 +59,7 @@
#include <xcb/xcb.h>
#include <X11/Xlib.h>
+#define WORK_AREA_MARGIN 12 // margin of the work area
#define MIN_SLIDE_INTERVAL 5*60000 // 5 min
#define MAX_SLIDE_INTERVAL (24*60+55)*60000 // 24 h and 55 min
@@ -751,7 +752,7 @@ void DesktopWindow::removeBottomGap() {
//qDebug() << "delegate:" << delegate->itemSize();
QSize cellMargins = getMargins();
int workAreaHeight = qApp->desktop()->availableGeometry(screenNum_).height()
- - 24; // a 12-pix margin will be considered everywhere
+ - 2 * WORK_AREA_MARGIN;
int cellHeight = itemSize.height() + listView_->spacing();
int iconNumber = workAreaHeight / cellHeight;
int bottomGap = workAreaHeight % cellHeight;
@@ -827,7 +828,7 @@ void DesktopWindow::relayoutItems() {
screen = screenNum_;
}
QRect workArea = desktop->availableGeometry(screen);
- workArea.adjust(12, 12, -12, -12); // add a 12 pixel margin to the work area
+ workArea.adjust(WORK_AREA_MARGIN, WORK_AREA_MARGIN, -WORK_AREA_MARGIN, -WORK_AREA_MARGIN);
// qDebug() << "workArea" << screen << workArea;
// FIXME: we use an internal class declared in a private header here, which is pretty bad.
QPoint pos = workArea.topLeft();
@@ -902,7 +903,7 @@ void DesktopWindow::loadItemPositions()
auto delegate = static_cast<Fm::FolderItemDelegate*>(listView_->itemDelegateForColumn(0));
auto grid = delegate->itemSize();
QRect workArea = qApp->desktop()->availableGeometry(screenNum_);
- workArea.adjust(12, 12, -12, -12);
+ workArea.adjust(WORK_AREA_MARGIN, WORK_AREA_MARGIN, -WORK_AREA_MARGIN, -WORK_AREA_MARGIN);
QString desktopDir = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
desktopDir += '/';
std::vector<QPoint> usedPos;
@@ -1281,8 +1282,8 @@ void DesktopWindow::childDropEvent(QDrop
auto delegate = static_cast<Fm::FolderItemDelegate*>(listView_->itemDelegateForColumn(0));
auto grid = delegate->itemSize();
QRect workArea = qApp->desktop()->availableGeometry(screenNum_);
- workArea.adjust(12, 12, -12, -12);
- QPoint pos = mapFromGlobal(e->pos());
+ workArea.adjust(WORK_AREA_MARGIN, WORK_AREA_MARGIN, -WORK_AREA_MARGIN, -WORK_AREA_MARGIN);
+ QPoint pos = e->pos();
const QModelIndexList indexes = selectedIndexes();
for(const QModelIndex& indx : indexes) {
if(auto file = proxyModel_->fileInfoFromIndex(indx)) {
@@ -1305,9 +1306,9 @@ void DesktopWindow::childDropEvent(QDrop
auto delegate = static_cast<Fm::FolderItemDelegate*>(listView_->itemDelegateForColumn(0));
auto grid = delegate->itemSize();
QRect workArea = qApp->desktop()->availableGeometry(screenNum_);
- workArea.adjust(12, 12, -12, -12);
+ workArea.adjust(WORK_AREA_MARGIN, WORK_AREA_MARGIN, -WORK_AREA_MARGIN, -WORK_AREA_MARGIN);
const QString desktopDir = XdgDir::readDesktopDir() + QString(QLatin1String("/"));
- QPoint pos = mapFromGlobal(e->pos());
+ QPoint pos = e->pos();
const QList<QUrl> urlList = mimeData->urls();
for(const QUrl& url : urlList) {
QString name = url.fileName();
@@ -1352,12 +1353,10 @@ void DesktopWindow::stickToPosition(cons
}
void DesktopWindow::alignToGrid(QPoint& pos, const QPoint& topLeft, const QSize& grid, const int spacing) {
- qreal w = qAbs((qreal)pos.x() - (qreal)topLeft.x())
- / (qreal)(grid.width() + spacing);
- qreal h = qAbs((qreal)pos.y() - (qreal)topLeft.y())
- / (qreal)(grid.height() + spacing);
- pos.setX(topLeft.x() + qRound(w) * (grid.width() + spacing));
- pos.setY(topLeft.y() + qRound(h) * (grid.height() + spacing));
+ int w = qAbs(pos.x() - topLeft.x()) / (grid.width() + spacing);
+ int h = qAbs(pos.y() - topLeft.y()) / (grid.height() + spacing);
+ pos.setX(topLeft.x() + w * (grid.width() + spacing));
+ pos.setY(topLeft.y() + h * (grid.height() + spacing));
}
void DesktopWindow::closeEvent(QCloseEvent* event) {

@ -0,0 +1,2 @@
polish-dnd-1.patch
polish-dnd-2.patch
Loading…
Cancel
Save