Skip to content

Commit 0ca2e48

Browse files
korneel-emwebRockinRoel
authored andcommitted
WT-8638: drag/drop between rows in WAbstractItemView
1 parent c07552c commit 0ca2e48

File tree

16 files changed

+485
-43
lines changed

16 files changed

+485
-43
lines changed

resources/themes/bootstrap/2/wt.css

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,26 @@
644644
background-color: #EEEEEE;
645645
outline: 1px dotted black;
646646
}
647+
648+
.Wt-drop-site-top {
649+
box-shadow: 0 2px 0 #000 inset;
650+
position: absolute;
651+
top: 0px;
652+
left: 0px;
653+
width:100%;
654+
height:100%;
655+
pointer-events: none;
656+
}
657+
.Wt-drop-site-bottom {
658+
box-shadow: 0 -2px 0 #000 inset;
659+
position: absolute;
660+
top: 0px;
661+
left: 0px;
662+
width:100%;
663+
height:100%;
664+
pointer-events: none;
665+
}
666+
647667
body.Wt-rtl .Wt-itemview .Wt-tv-rh,
648668
body.Wt-rtl .Wt-itemview .Wt-tv-sh {
649669
float: left;

resources/themes/bootstrap/3/wt.css

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,25 @@ fieldset[disabled] .Wt-slider-v .handle.active {
712712
outline: 1px dotted black;
713713
}
714714

715+
.Wt-drop-site-top {
716+
box-shadow: 0 2px 0 #000 inset;
717+
position: absolute;
718+
top: 0px;
719+
left: 0px;
720+
width:100%;
721+
height:100%;
722+
pointer-events: none;
723+
}
724+
.Wt-drop-site-bottom {
725+
box-shadow: 0 -2px 0 #000 inset;
726+
position: absolute;
727+
top: 0px;
728+
left: 0px;
729+
width:100%;
730+
height:100%;
731+
pointer-events: none;
732+
}
733+
715734
body.Wt-rtl .Wt-itemview .Wt-tv-rh,
716735
body.Wt-rtl .Wt-itemview .Wt-tv-sh {
717736
float: left;

resources/themes/default/wt.css

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -931,6 +931,26 @@ body.Wt-rtl .Wt-itemview .Wt-tv-sh {
931931
outline: 1px dotted black;
932932
}
933933

934+
.Wt-drop-site-top {
935+
box-shadow: 0 2px 0 #000 inset;
936+
position: absolute;
937+
top: 0px;
938+
left: 0px;
939+
width:100%;
940+
height:100%;
941+
pointer-events: none;
942+
}
943+
.Wt-drop-site-bottom {
944+
box-shadow: 0 -2px 0 #000 inset;
945+
position: absolute;
946+
top: 0px;
947+
left: 0px;
948+
width:100%;
949+
height:100%;
950+
pointer-events: none;
951+
}
952+
953+
934954
/*
935955
* WTableView
936956
*/

resources/themes/polished/wt.css

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -995,6 +995,25 @@ body.Wt-rtl .Wt-itemview .Wt-tv-sh {
995995
outline: 1px dotted black;
996996
}
997997

998+
.Wt-drop-site-top {
999+
box-shadow: 0 2px 0 #000 inset;
1000+
position: absolute;
1001+
top: 0px;
1002+
left: 0px;
1003+
width:100%;
1004+
height:100%;
1005+
pointer-events: none;
1006+
}
1007+
.Wt-drop-site-bottom {
1008+
box-shadow: 0 -2px 0 #000 inset;
1009+
position: absolute;
1010+
top: 0px;
1011+
left: 0px;
1012+
width:100%;
1013+
height:100%;
1014+
pointer-events: none;
1015+
}
1016+
9981017
/*
9991018
* WTableView
10001019
*/

src/Wt/WAbstractItemModel.C

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -290,8 +290,6 @@ void WAbstractItemModel::dropEvent(const WDropEvent& e, DropAction action,
290290
}
291291

292292
++r;
293-
} else {
294-
295293
}
296294
}
297295

@@ -311,6 +309,66 @@ void WAbstractItemModel::dropEvent(const WDropEvent& e, DropAction action,
311309
}
312310
}
313311

312+
void WAbstractItemModel::dropEvent(const WDropEvent& e, DropAction action,
313+
const WModelIndex& pindex, Wt::Side side)
314+
{
315+
WItemSelectionModel *selectionModel
316+
= dynamic_cast<WItemSelectionModel *>(e.source());
317+
if (selectionModel) {
318+
auto sourceModel = selectionModel->model();
319+
320+
const WModelIndex& parent = pindex.parent();
321+
int row = !pindex.isValid() ? rowCount() :
322+
side == Side::Bottom ? pindex.row()+1 : pindex.row();
323+
324+
/*
325+
* (1) Insert new rows (or later: cells ?)
326+
*/
327+
if (!insertRows(row, selectionModel->selectedIndexes().size(), parent)) {
328+
LOG_ERROR("dropEvent(): could not insertRows()");
329+
return;
330+
}
331+
332+
/*
333+
* (2) Copy data
334+
*/
335+
WModelIndexSet selection = selectionModel->selectedIndexes();
336+
337+
int r = row;
338+
for (WModelIndexSet::const_iterator i = selection.begin();
339+
i != selection.end(); ++i) {
340+
WModelIndex sourceIndex = *i;
341+
if (selectionModel->selectionBehavior() ==
342+
SelectionBehavior::Rows) {
343+
WModelIndex sourceParent = sourceIndex.parent();
344+
345+
for (int col = 0; col < sourceModel->columnCount(sourceParent); ++col) {
346+
WModelIndex s = sourceModel->index(sourceIndex.row(), col,
347+
sourceParent);
348+
WModelIndex d = index(r, col, parent);
349+
copyData(sourceModel.get(), s, this, d);
350+
}
351+
352+
++r;
353+
}
354+
}
355+
356+
/*
357+
* (3) Remove original data
358+
*/
359+
if (action == DropAction::Move) {
360+
while (!selectionModel->selectedIndexes().empty()) {
361+
WModelIndex i = Utils::last(selectionModel->selectedIndexes());
362+
363+
if (!sourceModel->removeRow(i.row(), i.parent())) {
364+
LOG_ERROR("dropEvent(): could not removeRows()");
365+
return;
366+
}
367+
}
368+
}
369+
}
370+
}
371+
314372
void WAbstractItemModel::beginInsertColumns(const WModelIndex& parent,
315373
int first, int last)
316374
{

src/Wt/WAbstractItemModel.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,12 @@ class WT_API WAbstractItemModel : public WObject
555555
* abstract item models. Source item data is copied (but not the
556556
* source item's flags).
557557
*
558+
* This method is overloaded for handling drop events on top of items
559+
* or drop events between items (see Wt::DropLocation). This overload
560+
* handles drops on top of items, but note that due to historical
561+
* reasons it will also insert the items in between when called with
562+
* DropAction::Move.
563+
*
558564
* The location in the model is indicated by the \p row and
559565
* \p column within the \p parent index. If \p row is
560566
* -1, then the item is appended to the \p parent. Otherwise,
@@ -575,6 +581,29 @@ class WT_API WAbstractItemModel : public WObject
575581
virtual void dropEvent(const WDropEvent& e, DropAction action,
576582
int row, int column, const WModelIndex& parent);
577583

584+
/*! \brief Handles a drop event.
585+
*
586+
* The default implementation only handles generic drag&drop between
587+
* abstract item models. Source item data is copied (but not the
588+
* source item's flags).
589+
*
590+
* This method is overloaded for handling drop events on top of items
591+
* or drop events between items. This overload handles drops between
592+
* items. The drop was received relative to the \p index item and the \p side
593+
* parameter will only be Wt::Top or Wt::Bottom.
594+
*
595+
* You may want to reimplement this method if you want to handle
596+
* other mime-type data, or if you want to refine how the drop event
597+
* of an item selection must be interpreted.
598+
*
599+
* \note Currently, only row selections are handled by the default
600+
* implementation.
601+
*
602+
* \sa mimeType(), WItemSelectionModel
603+
*/
604+
virtual void dropEvent(const WDropEvent& e, DropAction action,
605+
const WModelIndex& index, Wt::Side side);
606+
578607
/*! \brief Inserts one column.
579608
*
580609
* This is a convenience method that adds a single column, and is

src/Wt/WAbstractItemView.C

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,6 @@ WAbstractItemView::WAbstractItemView()
226226
renderState_(RenderState::NeedRerender),
227227
currentSortColumn_(-1),
228228
dragEnabled_(false),
229-
dropsEnabled_(false),
230229
selectionModel_(new WItemSelectionModel()),
231230
rowHeight_(20),
232231
headerLineHeight_(20),
@@ -575,10 +574,10 @@ void WAbstractItemView::dropEvent(const WDropEvent& e, const WModelIndex& index)
575574
* Here, we only handle standard drag&drop actions between abstract
576575
* item models.
577576
*/
578-
if (dropsEnabled_) {
577+
if (enabledDropLocations_.test(DropLocation::OnItem)) {
579578
std::vector<std::string> acceptMimeTypes = model_->acceptDropMimeTypes();
580579

581-
for (unsigned i = 0; i < acceptMimeTypes.size(); ++i)
580+
for (std::size_t i = 0; i < acceptMimeTypes.size(); ++i) {
582581
if (acceptMimeTypes[i] == e.mimeType()) {
583582
// we define internal by sharing the same selection model...
584583
// currently selection models cannot be shared
@@ -587,15 +586,31 @@ void WAbstractItemView::dropEvent(const WDropEvent& e, const WModelIndex& index)
587586
DropAction action = internal ?
588587
DropAction::Move : DropAction::Copy;
589588

590-
// TODO: (later) we need to interpret the event location
591-
// (e.mouseEvent().widget().y) For now we will implement to
592-
// add as a sibling before the index
593589
model_->dropEvent(e, action,
594590
index.row(), index.column(), index.parent());
595591

596592
setSelectedIndexes(WModelIndexSet());
597593
return;
598594
}
595+
}
596+
}
597+
598+
WCompositeWidget::dropEvent(e);
599+
}
600+
601+
void WAbstractItemView::dropEvent(const WDropEvent& e, const WModelIndex& index, Side side)
602+
{
603+
if (enabledDropLocations_.test(DropLocation::BetweenRows)) {
604+
std::vector<std::string> acceptMimeTypes = model_->acceptDropMimeTypes();
605+
606+
for (std::size_t i = 0; i < acceptMimeTypes.size(); ++i) {
607+
if (acceptMimeTypes[i] == e.mimeType()) {
608+
model_->dropEvent(e, DropAction::Move, index, side);
609+
610+
setSelectedIndexes(WModelIndexSet());
611+
return;
612+
}
613+
}
599614
}
600615

601616
WCompositeWidget::dropEvent(e);
@@ -617,7 +632,7 @@ void WAbstractItemView::configureModelDragDrop()
617632
std::vector<std::string> acceptMimeTypes = model_->acceptDropMimeTypes();
618633

619634
for (unsigned i = 0; i < acceptMimeTypes.size(); ++i)
620-
if (dropsEnabled_)
635+
if (!enabledDropLocations_.empty())
621636
acceptDrops(acceptMimeTypes[i], "Wt-drop-site");
622637
else
623638
stopAcceptDrops(acceptMimeTypes[i]);
@@ -626,11 +641,20 @@ void WAbstractItemView::configureModelDragDrop()
626641

627642
void WAbstractItemView::setDropsEnabled(bool enable)
628643
{
629-
if (dropsEnabled_ != enable) {
630-
dropsEnabled_ = enable;
644+
if (enable)
645+
setEnabledDropLocations(DropLocation::OnItem);
646+
else
647+
setEnabledDropLocations(None);
648+
}
631649

632-
configureModelDragDrop();
633-
}
650+
void WAbstractItemView::setEnabledDropLocations(WFlags<DropLocation> dropLocations) {
651+
if (enabledDropLocations_ == dropLocations)
652+
return;
653+
654+
enabledDropLocations_ = dropLocations;
655+
configureModelDragDrop();
656+
657+
scheduleRender();
634658
}
635659

636660
void WAbstractItemView::checkDragSelection()

0 commit comments

Comments
 (0)