mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-01 23:54:13 +02:00
Merge pull request #1199 from ispyisail/feature/terminal-strip-drag-drop
Add drag and drop of free terminals onto a terminal strip (#409)
This commit is contained in:
@@ -41,6 +41,9 @@ FreeTerminalEditor::FreeTerminalEditor(QETProject *project, QWidget *parent) :
|
||||
m_model = new FreeTerminalModel(m_project, this);
|
||||
ui->m_table_view->setModel(m_model);
|
||||
ui->m_table_view->setCurrentIndex(m_model->index(0,0));
|
||||
//Selected rows can be dragged onto a strip of the terminal strip tree
|
||||
ui->m_table_view->setDragEnabled(true);
|
||||
ui->m_table_view->setDragDropMode(QAbstractItemView::DragOnly);
|
||||
|
||||
if (m_project) {
|
||||
connect(m_project, &QObject::destroyed, this, &FreeTerminalEditor::reload);
|
||||
@@ -274,6 +277,14 @@ void FreeTerminalEditor::on_m_move_pb_clicked()
|
||||
reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief FreeTerminalEditor::hasPendingEdits
|
||||
* @return true if cells were edited and not yet applied
|
||||
*/
|
||||
bool FreeTerminalEditor::hasPendingEdits() const {
|
||||
return !m_model->modifiedModelRealTerminalData().isEmpty();
|
||||
}
|
||||
|
||||
void FreeTerminalEditor::selectionChanged()
|
||||
{
|
||||
const bool has_selection = !ui->m_table_view->selectionModel()->selectedIndexes().isEmpty();
|
||||
|
||||
@@ -39,6 +39,7 @@ class FreeTerminalEditor : public QWidget
|
||||
|
||||
void reload();
|
||||
void apply();
|
||||
bool hasPendingEdits() const;
|
||||
|
||||
void setProject(QETProject *project);
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <QComboBox>
|
||||
#include <QMimeData>
|
||||
|
||||
#include "freeterminalmodel.h"
|
||||
#include "../../elementprovider.h"
|
||||
@@ -239,7 +240,7 @@ QVariant FreeTerminalModel::headerData(int section, Qt::Orientation orientation,
|
||||
|
||||
Qt::ItemFlags FreeTerminalModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
|
||||
Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
|
||||
|
||||
auto c = index.column();
|
||||
if (c == LABEL_CELL || c == TYPE_CELL || c == FUNCTION_CELL)
|
||||
@@ -320,6 +321,73 @@ QVector<QSharedPointer<RealTerminal> > FreeTerminalModel::realTerminalForIndex(c
|
||||
return vector_;
|
||||
}
|
||||
|
||||
QStringList FreeTerminalModel::mimeTypes() const {
|
||||
return QStringList{freeTerminalMimeType()};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief FreeTerminalModel::mimeData
|
||||
* @param indexes
|
||||
* @return the uuids of the terminal elements of the rows in @a indexes,
|
||||
* to be dropped on a terminal strip of the terminal strip tree.
|
||||
*/
|
||||
QMimeData *FreeTerminalModel::mimeData(const QModelIndexList &indexes) const
|
||||
{
|
||||
QVector<QUuid> uuids;
|
||||
for (const auto &real_t : realTerminalForIndex(indexes)) {
|
||||
uuids.append(real_t->elementUuid());
|
||||
}
|
||||
return mimeDataForUuids(uuids);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief FreeTerminalModel::freeTerminalMimeType
|
||||
* @return the mime type of a drag of free terminals
|
||||
*/
|
||||
QString FreeTerminalModel::freeTerminalMimeType() {
|
||||
return QStringLiteral("application/x-qet-free-terminal-uuids");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief FreeTerminalModel::uuidsFromMimeData
|
||||
* @param mime_data
|
||||
* @return the uuids of the terminal elements carried by @a mime_data,
|
||||
* empty if it is not a drag of free terminals.
|
||||
*/
|
||||
QVector<QUuid> FreeTerminalModel::uuidsFromMimeData(const QMimeData *mime_data)
|
||||
{
|
||||
QVector<QUuid> uuids;
|
||||
if (!mime_data || !mime_data->hasFormat(freeTerminalMimeType())) {
|
||||
return uuids;
|
||||
}
|
||||
|
||||
const auto lines = QString::fromLatin1(mime_data->data(freeTerminalMimeType())).split(QLatin1Char('\n'), Qt::SkipEmptyParts);
|
||||
for (const auto &line : lines) {
|
||||
const QUuid uuid(line);
|
||||
if (!uuid.isNull()) {
|
||||
uuids.append(uuid);
|
||||
}
|
||||
}
|
||||
return uuids;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief FreeTerminalModel::mimeDataForUuids
|
||||
* @param uuids
|
||||
* @return a new QMimeData carrying the uuids of terminal elements in @a uuids
|
||||
*/
|
||||
QMimeData *FreeTerminalModel::mimeDataForUuids(const QVector<QUuid> &uuids)
|
||||
{
|
||||
QStringList lines;
|
||||
for (const auto &uuid : uuids) {
|
||||
lines.append(uuid.toString());
|
||||
}
|
||||
|
||||
auto mime_data = new QMimeData();
|
||||
mime_data->setData(freeTerminalMimeType(), lines.join(QLatin1Char('\n')).toLatin1());
|
||||
return mime_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief FreeTerminalModel::fillTerminalVector
|
||||
*/
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "../../qetproject.h"
|
||||
|
||||
class RealTerminal;
|
||||
class QMimeData;
|
||||
|
||||
/**
|
||||
* @brief The FreeTerminalModel class
|
||||
@@ -61,6 +62,12 @@ class FreeTerminalModel : public QAbstractTableModel
|
||||
modelRealTerminalData dataAtRow(int row) const;
|
||||
QVector<QSharedPointer<RealTerminal>> realTerminalForIndex(const QModelIndexList &index_list) const;
|
||||
|
||||
QStringList mimeTypes() const override;
|
||||
QMimeData *mimeData(const QModelIndexList &indexes) const override;
|
||||
static QString freeTerminalMimeType();
|
||||
static QVector<QUuid> uuidsFromMimeData(const QMimeData *mime_data);
|
||||
static QMimeData *mimeDataForUuids(const QVector<QUuid> &uuids);
|
||||
|
||||
private:
|
||||
void fillTerminalVector();
|
||||
|
||||
|
||||
@@ -67,6 +67,10 @@ TerminalStripEditorWindow::TerminalStripEditorWindow(QETProject *project, QWidge
|
||||
m_terminal_strip_editor = new TerminalStripEditor{m_project, this};
|
||||
|
||||
connect(m_tree_dock, &TerminalStripTreeDockWidget::currentStripChanged, this, &TerminalStripEditorWindow::currentStripChanged);
|
||||
//A move reloads the free terminal table, which would lose its unapplied edits
|
||||
m_tree_dock->setDropCheck([this]() {
|
||||
return !m_free_terminal_editor->hasPendingEdits();
|
||||
});
|
||||
|
||||
ui->m_stacked_widget->insertWidget(EMPTY_PAGE, new QWidget(ui->m_stacked_widget));
|
||||
ui->m_stacked_widget->insertWidget(FREE_TERMINAL_PAGE, m_free_terminal_editor);
|
||||
|
||||
@@ -27,7 +27,13 @@
|
||||
#include "../../qetgraphicsitem/terminalelement.h"
|
||||
#include "../terminalstrip.h"
|
||||
#include "../../qetinformation.h"
|
||||
#include "freeterminalmodel.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDrag>
|
||||
#include <QDropEvent>
|
||||
#include <QMimeData>
|
||||
#include <QMouseEvent>
|
||||
#include <QUuid>
|
||||
|
||||
TerminalStripTreeDockWidget::TerminalStripTreeDockWidget(QETProject *project, QWidget *parent) :
|
||||
@@ -38,6 +44,12 @@ TerminalStripTreeDockWidget::TerminalStripTreeDockWidget(QETProject *project, QW
|
||||
setProject(project);
|
||||
|
||||
ui->m_tree_view->expandRecursively(ui->m_tree_view->rootIndex());
|
||||
|
||||
//Free terminals are dragged from this tree or from the free terminal
|
||||
//table and dropped on a strip. The tree items themselves never move,
|
||||
//the tree is rebuilt from the project after the drop.
|
||||
ui->m_tree_view->viewport()->setAcceptDrops(true);
|
||||
ui->m_tree_view->viewport()->installEventFilter(this);
|
||||
}
|
||||
|
||||
TerminalStripTreeDockWidget::~TerminalStripTreeDockWidget()
|
||||
@@ -365,3 +377,152 @@ void TerminalStripTreeDockWidget::setCurrentStrip(TerminalStrip *strip)
|
||||
m_current_strip = strip;
|
||||
emit currentStripChanged(strip);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TerminalStripTreeDockWidget::setDropCheck
|
||||
* @param check : called before accepting a drop of free terminals,
|
||||
* a drop is refused when it returns false.
|
||||
*/
|
||||
void TerminalStripTreeDockWidget::setDropCheck(std::function<bool ()> check) {
|
||||
m_drop_check = check;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TerminalStripTreeDockWidget::eventFilter
|
||||
* Drag a free terminal of the tree, and drop free terminals
|
||||
* (from the tree or from the free terminal table) on a strip.
|
||||
*/
|
||||
bool TerminalStripTreeDockWidget::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
if (watched != ui->m_tree_view->viewport()) {
|
||||
return QDockWidget::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
switch (event->type())
|
||||
{
|
||||
case QEvent::MouseButtonPress:
|
||||
{
|
||||
auto me = static_cast<QMouseEvent *>(event);
|
||||
m_drag_uuid = QUuid();
|
||||
auto item = ui->m_tree_view->itemAt(me->pos());
|
||||
if (me->button() == Qt::LeftButton
|
||||
&& item
|
||||
&& item->type() == Terminal
|
||||
&& item->parent()
|
||||
&& item->parent()->type() == FreeTerminal)
|
||||
{
|
||||
m_drag_start_pos = me->pos();
|
||||
m_drag_uuid = item->data(0, UUID_USER_ROLE).toUuid();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QEvent::MouseMove:
|
||||
{
|
||||
auto me = static_cast<QMouseEvent *>(event);
|
||||
if (!m_drag_uuid.isNull()
|
||||
&& (me->buttons() & Qt::LeftButton)
|
||||
&& (me->pos() - m_drag_start_pos).manhattanLength() >= QApplication::startDragDistance())
|
||||
{
|
||||
auto drag = new QDrag(ui->m_tree_view);
|
||||
drag->setMimeData(FreeTerminalModel::mimeDataForUuids({m_drag_uuid}));
|
||||
drag->setPixmap(QET::Icons::ElementTerminal.pixmap(16, 16));
|
||||
m_drag_uuid = QUuid();
|
||||
drag->exec(Qt::CopyAction);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QEvent::DragEnter:
|
||||
{
|
||||
//Accepted wherever it enters, or no DragMove follows to reach a strip
|
||||
auto de = static_cast<QDragEnterEvent *>(event);
|
||||
if (!freeTerminals(de->mimeData()).isEmpty()) {
|
||||
de->acceptProposedAction();
|
||||
} else {
|
||||
de->ignore();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case QEvent::DragMove:
|
||||
{
|
||||
auto de = static_cast<QDragMoveEvent *>(event);
|
||||
if (dropAllowed(de->mimeData(), de->position().toPoint())) {
|
||||
de->acceptProposedAction();
|
||||
} else {
|
||||
de->ignore();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case QEvent::Drop:
|
||||
{
|
||||
auto de = static_cast<QDropEvent *>(event);
|
||||
const auto pos = de->position().toPoint();
|
||||
if (!dropAllowed(de->mimeData(), pos)) {
|
||||
de->ignore();
|
||||
return true;
|
||||
}
|
||||
auto strip = stripAt(pos);
|
||||
const auto terminals = freeTerminals(de->mimeData());
|
||||
de->acceptProposedAction();
|
||||
|
||||
//The command rebuilds the tree (TerminalStrip::orderChanged)
|
||||
m_project->undoStack()->push(new AddTerminalToStripCommand(terminals, strip));
|
||||
setSelectedStrip(strip);
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return QDockWidget::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TerminalStripTreeDockWidget::stripAt
|
||||
* @param pos : position in the viewport of the tree
|
||||
* @return the strip of the item at @a pos, the item being the strip
|
||||
* or one of its terminals, or nullptr.
|
||||
*/
|
||||
TerminalStrip *TerminalStripTreeDockWidget::stripAt(const QPoint &pos) const
|
||||
{
|
||||
auto item = ui->m_tree_view->itemAt(pos);
|
||||
if (item && item->type() == Terminal) {
|
||||
item = item->parent();
|
||||
}
|
||||
if (item && item->type() == Strip) {
|
||||
return m_item_strip_H.value(item);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TerminalStripTreeDockWidget::freeTerminals
|
||||
* @param mime_data
|
||||
* @return the free terminals carried by @a mime_data
|
||||
* which are still free in the project.
|
||||
*/
|
||||
QVector<QSharedPointer<RealTerminal>> TerminalStripTreeDockWidget::freeTerminals(const QMimeData *mime_data) const
|
||||
{
|
||||
QVector<QSharedPointer<RealTerminal>> terminals;
|
||||
for (const auto &uuid : FreeTerminalModel::uuidsFromMimeData(mime_data))
|
||||
{
|
||||
const auto real_t = m_uuid_terminal_H.value(uuid);
|
||||
if (real_t && !real_t->parentStrip() && !terminals.contains(real_t)) {
|
||||
terminals.append(real_t);
|
||||
}
|
||||
}
|
||||
return terminals;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TerminalStripTreeDockWidget::dropAllowed
|
||||
* @return true if @a mime_data carries free terminals
|
||||
* and there is a strip at @a pos to drop them in.
|
||||
*/
|
||||
bool TerminalStripTreeDockWidget::dropAllowed(const QMimeData *mime_data, const QPoint &pos) const
|
||||
{
|
||||
return m_project
|
||||
&& stripAt(pos)
|
||||
&& !freeTerminals(mime_data).isEmpty()
|
||||
&& (!m_drop_check || m_drop_check());
|
||||
}
|
||||
|
||||
@@ -21,11 +21,15 @@
|
||||
#include <QDockWidget>
|
||||
#include <QPointer>
|
||||
#include <QHash>
|
||||
#include <QUuid>
|
||||
|
||||
#include <functional>
|
||||
|
||||
class QETProject;
|
||||
class QTreeWidgetItem;
|
||||
class TerminalStrip;
|
||||
class RealTerminal;
|
||||
class QMimeData;
|
||||
|
||||
namespace Ui {
|
||||
class TerminalStripTreeDockWidget;
|
||||
@@ -58,6 +62,10 @@ class TerminalStripTreeDockWidget : public QDockWidget
|
||||
QString currentLocation() const;
|
||||
void setSelectedStrip(TerminalStrip *strip);
|
||||
QSharedPointer<RealTerminal> currentRealTerminal() const;
|
||||
void setDropCheck(std::function<bool()> check);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
|
||||
signals:
|
||||
void currentStripChanged(TerminalStrip *strip);
|
||||
@@ -70,6 +78,9 @@ class TerminalStripTreeDockWidget : public QDockWidget
|
||||
QTreeWidgetItem* addTerminalStrip(TerminalStrip *terminal_strip);
|
||||
void addFreeTerminal();
|
||||
void setCurrentStrip(TerminalStrip *strip);
|
||||
TerminalStrip *stripAt(const QPoint &pos) const;
|
||||
QVector<QSharedPointer<RealTerminal>> freeTerminals(const QMimeData *mime_data) const;
|
||||
bool dropAllowed(const QMimeData *mime_data, const QPoint &pos) const;
|
||||
|
||||
private:
|
||||
Ui::TerminalStripTreeDockWidget *ui;
|
||||
@@ -82,6 +93,9 @@ class TerminalStripTreeDockWidget : public QDockWidget
|
||||
QVector<QMetaObject::Connection> m_strip_changed_connection;
|
||||
bool m_current_is_free_terminal{false};
|
||||
QMetaObject::Connection m_project_destroy_connection;
|
||||
std::function<bool()> m_drop_check;
|
||||
QPoint m_drag_start_pos;
|
||||
QUuid m_drag_uuid;
|
||||
};
|
||||
|
||||
#endif // TERMINALSTRIPTREEDOCKWIDGET_H
|
||||
|
||||
Reference in New Issue
Block a user