Place an element from the collection without dragging it

DiagramEventAddElement is already a good placement mode: the element
follows the cursor on the grid, a left click drops it, Space rotates it,
and it stays loaded for a run of the same symbol. Its only caller was
DiagramView::handleElementDrop(), so it could be reached only by
finishing a drag. Double-clicking a symbol in the Collections dock
opened the element editor instead.

- DiagramView::startElementPlacement() is split out of
  handleElementDrop(). defaultPlacementPos() uses the cursor when it is
  over the view and the centre of the visible area otherwise.

- ElementsCollectionWidget emits insertElementRequested() on double
  click, or Enter on the highlighted item. The host decides which view
  receives it, so the widget can later be reused outside the editor.

- When there is nowhere to place it (no folio open, read-only project),
  the element editor opens, as a double click did before.

- "Insérer le dernier élément" (Édition menu, default key A) places the
  last element again. DiagramView reports every placement it starts, so
  an element dropped by drag counts too. Macros are not remembered.

A rather than Space: Space rotates the pending element inside placement
mode and is bound three more times in this editor. The key is a
ShortcutManager default and can be changed in the Shortcuts page.

Double click placing is a behaviour change, so it has a preference,
"elementscollection/double-click-inserts" (default true), shown in
Configuration as an opt-out: "Double-cliquer dans la collection ouvre
l'éditeur d'élément au lieu de l'insérer".

Discussions #676 and #1033.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG
This commit is contained in:
ispyisail
2026-08-08 17:18:30 +12:00
parent 4cffdf93b6
commit e6291eb8e8
8 changed files with 259 additions and 23 deletions
@@ -35,6 +35,8 @@
#include <QCheckBox>
#include <QDesktopServices>
#include <QSettings>
#include <QShortcut>
#include <QDialog>
#include <QDialogButtonBox>
#include <QFileDialog>
@@ -266,15 +268,7 @@ void ElementsCollectionWidget::setUpConnection()
this, &ElementsCollectionWidget::dirProperties);
connect(m_tree_view, &QTreeView::doubleClicked,
[this](const QModelIndex &index)
{
this->m_index_at_context_menu = index ;
ElementCollectionItem *eci = elementCollectionItemForIndex(index);
if (eci && eci->collectionPath().endsWith(".qetmak")) {
return; // Do nothing on double click for macros
}
this->editElement();
});
[this](const QModelIndex &index) { this->activateIndex(index); });
connect(m_tree_view, &QTreeView::entered,
[this] (const QModelIndex &index) {
@@ -284,19 +278,23 @@ void ElementsCollectionWidget::setUpConnection()
qde->statusBar()->showMessage(eci->localName());
});
//Enter on the highlighted item does the same as a double click, so a
//run of elements can be placed without leaving the keyboard. Bound as
//a shortcut on the view rather than by reimplementing keyPressEvent,
//which would mean subclassing ElementsTreeView for one key.
for (const auto key : {Qt::Key_Return, Qt::Key_Enter}) {
auto *sc = new QShortcut(QKeySequence(key), m_tree_view);
sc->setContext(Qt::WidgetShortcut);
connect(sc, &QShortcut::activated, this, [this]() {
this->activateIndex(m_tree_view->currentIndex());
});
}
connect(m_macros_tree_view, &QTreeView::customContextMenuRequested,
this, &ElementsCollectionWidget::customContextMenu);
connect(m_macros_tree_view, &QTreeView::doubleClicked,
[this](const QModelIndex &index)
{
this->m_index_at_context_menu = index ;
ElementCollectionItem *eci = elementCollectionItemForIndex(index);
if (eci && eci->collectionPath().endsWith(".qetmak")) {
return; // Do nothing on double click for macros
}
this->editElement();
});
[this](const QModelIndex &index) { this->activateIndex(index); });
connect(m_macros_tree_view, &QTreeView::entered,
[this] (const QModelIndex &index) {
@@ -405,6 +403,60 @@ void ElementsCollectionWidget::openDir()
}
/**
@brief ElementsCollectionWidget::activateIndex
What a double click (or Enter) on @a index does.
Historically this opened the element editor, which is the slowest action
available on a symbol you are most likely about to place. Placing is now
the default and editing has moved to the context menu, where it already
was. The old behaviour is preserved behind a preference for anyone who
relies on it.
@param index
*/
void ElementsCollectionWidget::activateIndex(const QModelIndex &index)
{
m_index_at_context_menu = index;
ElementCollectionItem *eci = elementCollectionItemForIndex(index);
if (!eci) {
return;
}
//Macros are placed, never edited, and were already skipped here.
const bool is_macro = eci->collectionPath().endsWith(".qetmak");
QSettings settings;
const bool insert = settings.value(
QStringLiteral("elementscollection/double-click-inserts"), true).toBool();
if (insert) {
insertCurrentElement();
return;
}
if (!is_macro) {
editElement();
}
}
/**
@brief ElementsCollectionWidget::insertCurrentElement
Ask for the current item to be placed on the folio.
*/
void ElementsCollectionWidget::insertCurrentElement()
{
ElementCollectionItem *eci =
elementCollectionItemForIndex(m_index_at_context_menu);
if (!(eci && eci->isElement())) {
return;
}
ElementsLocation location(eci->collectionPath());
if (!location.exist()) {
return;
}
emit insertElementRequested(location);
}
/**
@brief ElementsCollectionWidget::editElement
Edit the element represented by the current selected item
@@ -415,10 +467,19 @@ void ElementsCollectionWidget::editElement()
if ( !(eci && eci->isElement()) ) return;
// Prevent the element editor from opening for macros
if (eci->collectionPath().endsWith(".qetmak")) return;
editLocation(ElementsLocation(eci->collectionPath()));
}
ElementsLocation location(eci->collectionPath());
/**
@brief ElementsCollectionWidget::editLocation
Open the element editor on @a location. Macros have no editor and are
ignored.
@param location
*/
void ElementsCollectionWidget::editLocation(const ElementsLocation &location)
{
// Prevent the element editor from opening for macros
if (!location.exist() || location.path().endsWith(".qetmak")) return;
QETApp *app = QETApp::instance();
app->openElementLocations(QList<ElementsLocation>() << location);