Drag and drop layers in the layer manager

This commit is contained in:
Dan Paulat 2023-10-15 09:09:47 -05:00
parent 14d6912014
commit ec2663aee6
3 changed files with 153 additions and 4 deletions

View file

@ -4,14 +4,16 @@
#include <scwx/qt/types/qt_types.hpp>
#include <scwx/util/logger.hpp>
#include <set>
#include <variant>
#include <QApplication>
#include <QCheckBox>
#include <QFontMetrics>
#include <QIODevice>
#include <QMimeData>
#include <QStyle>
#include <QStyleOption>
#include <boost/container/devector.hpp>
namespace scwx
{
@ -30,6 +32,8 @@ static constexpr int kNumColumns = kLastColumn - kFirstColumn + 1;
static constexpr std::size_t kMapCount_ = 4u;
static const QString kMimeFormat {"application/x.scwx-layer-model"};
typedef std::
variant<std::monostate, types::Layer, awips::Phenomenon, std::string>
LayerDescription;
@ -45,7 +49,7 @@ public:
std::array<bool, kMapCount_> displayed_ {true, true, true, true};
};
typedef boost::container::devector<LayerInfo> LayerVector;
typedef std::vector<LayerInfo> LayerVector;
explicit Impl(LayerModel* self) : self_ {self}
{
@ -145,9 +149,21 @@ Qt::ItemFlags LayerModel::flags(const QModelIndex& index) const
break;
}
if (layer.movable_)
{
flags |= Qt::ItemFlag::ItemIsDragEnabled;
}
flags |= Qt::ItemFlag::ItemIsDropEnabled;
return flags;
}
Qt::DropActions LayerModel::supportedDropActions() const
{
return Qt::DropAction::MoveAction;
}
QVariant LayerModel::data(const QModelIndex& index, int role) const
{
static const QString enabledString = QObject::tr("Enabled");
@ -390,6 +406,117 @@ bool LayerModel::setData(const QModelIndex& index,
return result;
}
QStringList LayerModel::mimeTypes() const
{
return {kMimeFormat};
}
QMimeData* LayerModel::mimeData(const QModelIndexList& indexes) const
{
// Get parent QMimeData
QMimeData* mimeData = QAbstractTableModel::mimeData(indexes);
// Generate LayerModel data
QByteArray data {};
QDataStream stream(&data, QIODevice::WriteOnly);
std::set<int> rows {};
for (auto& index : indexes)
{
if (!rows.contains(index.row()))
{
rows.insert(index.row());
stream << index.row();
}
}
// Set LayerModel data in QMimeData
mimeData->setData(kMimeFormat, data);
return mimeData;
}
bool LayerModel::dropMimeData(const QMimeData* data,
Qt::DropAction /* action */,
int /* row */,
int /* column */,
const QModelIndex& parent)
{
QByteArray mimeData = data->data(kMimeFormat);
QDataStream stream(&mimeData, QIODevice::ReadOnly);
std::vector<int> sourceRows {};
// Read source rows from QMimeData
while (!stream.atEnd())
{
int sourceRow;
stream >> sourceRow;
sourceRows.push_back(sourceRow);
}
// Ensure rows are in numerical order
std::sort(sourceRows.begin(), sourceRows.end());
if (sourceRows.back() >= p->layers_.size())
{
logger_->error("Cannot perform drop action, invalid source rows");
return false;
}
// Nothing to insert
if (sourceRows.empty())
{
return false;
}
// Create a copy of the layers to insert (don't insert in-place)
std::vector<Impl::LayerInfo> newLayers {};
for (auto& sourceRow : sourceRows)
{
newLayers.push_back(p->layers_.at(sourceRow));
}
// Insert the copied layers
auto insertPosition = p->layers_.begin() + parent.row();
beginInsertRows(QModelIndex(),
parent.row(),
parent.row() + static_cast<int>(sourceRows.size()) - 1);
p->layers_.insert(insertPosition, newLayers.begin(), newLayers.end());
endInsertRows();
return true;
}
bool LayerModel::removeRows(int row, int count, const QModelIndex& parent)
{
// Validate count
if (count <= 0)
{
return false;
}
// Remove rows
auto erasePosition = p->layers_.begin() + row;
for (int i = 0; i < count; ++i)
{
if (erasePosition->movable_)
{
// Remove the current row if movable
beginRemoveRows(parent, row, row);
erasePosition = p->layers_.erase(erasePosition);
endRemoveRows();
}
else
{
// Don't remove immovable rows
++erasePosition;
++row;
}
}
return true;
}
void LayerModel::HandlePlacefileRemoved(const std::string& name)
{
auto it =
@ -489,7 +616,7 @@ void LayerModel::Impl::AddPlacefile(const std::string& name)
// Placefile is new, add row
self_->beginInsertRows(QModelIndex(), 0, 0);
layers_.insert(insertPosition, {types::LayerType::Placefile, name});
layers_.insert(insertPosition, {types::LayerType::Placefile, name, true});
self_->endInsertRows();
}

View file

@ -40,7 +40,8 @@ public:
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
Qt::ItemFlags flags(const QModelIndex& index) const override;
Qt::ItemFlags flags(const QModelIndex& index) const override;
Qt::DropActions supportedDropActions() const override;
QVariant data(const QModelIndex& index,
int role = Qt::DisplayRole) const override;
@ -52,6 +53,18 @@ public:
const QVariant& value,
int role = Qt::EditRole) override;
QStringList mimeTypes() const override;
QMimeData* mimeData(const QModelIndexList& indexes) const override;
bool dropMimeData(const QMimeData* data,
Qt::DropAction action,
int row,
int column,
const QModelIndex& parent) override;
bool removeRows(int row,
int count,
const QModelIndex& parent = QModelIndex()) override;
public slots:
void HandlePlacefileRemoved(const std::string& name);
void HandlePlacefileRenamed(const std::string& oldName,

View file

@ -37,6 +37,15 @@
</property>
<item>
<widget class="QTreeView" name="layerTreeView">
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::InternalMove</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::MoveAction</enum>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>