diff --git a/sources/diagram.cpp b/sources/diagram.cpp index dacc27d4e..47dd2f3e8 100644 --- a/sources/diagram.cpp +++ b/sources/diagram.cpp @@ -450,6 +450,34 @@ void Diagram::wheelEvent(QGraphicsSceneWheelEvent *event) QGraphicsScene::wheelEvent(event); } +/** + @brief Diagram::event + QGraphicsScene has its own Tab/Shift+Tab item-focus-chain traversal + (mirroring QWidget's), checked before keyPressEvent() is ever reached: + by default it would silently consume Tab/Backtab to move focus among + the scene's own focusable items. Intercepting the key press here, + ahead of that, is the only way to reliably override it: unlike + QWidget::focusNextPrevChild(), QGraphicsScene::focusNextPrevChild() is + only virtual starting in Qt 6 (guarded by the QT6_VIRTUAL macro), so a + Diagram:: override of it would silently do nothing on a Qt 5 build. + @param event +*/ +bool Diagram::event(QEvent *event) +{ + if (event->type() == QEvent::KeyPress) { + auto *key_event = static_cast(event); + if ((key_event->key() == Qt::Key_Tab || key_event->key() == Qt::Key_Backtab) + && !isReadOnly() && !focusItem()) { + bool forward = key_event->key() == Qt::Key_Tab + && !(key_event->modifiers() & Qt::ShiftModifier); + selectNextItem(forward); + event->accept(); + return true; + } + } + return QGraphicsScene::event(event); +} + /** @brief Diagram::keyPressEvent This event is managed by diagram event interface if any. @@ -1902,6 +1930,85 @@ void Diagram::invertSelection() emit selectionChanged(); } +/** + @brief Diagram::selectAllConductors + Select every conductor on this diagram, deselecting anything else. +*/ +void Diagram::selectAllConductors() +{ + if (items().isEmpty()) return; + + blockSignals(true); + for (auto item : items()) { + item -> setSelected(dynamic_cast(item) != nullptr); + } + blockSignals(false); + emit selectionChanged(); +} + +/** + @brief Diagram::selectAllTextFields + Select every text field on this diagram (independent/static text, + conductor labels, and dynamic element texts), deselecting anything else. +*/ +void Diagram::selectAllTextFields() +{ + if (items().isEmpty()) return; + + blockSignals(true); + for (auto item : items()) { + item -> setSelected(dynamic_cast(item) != nullptr); + } + blockSignals(false); + emit selectionChanged(); +} + +/** + @brief Diagram::selectNextItem + Select the next (or, if @a forward is false, the previous) selectable + item on this diagram, cycling through items() (z-order) and wrapping + around at either end. If nothing is currently selected, selects the + first (or last) item. Uses the same "what counts as a real selectable + diagram item" filter as invertSelection(), so the candidate list and + its order always match what the user could reach by clicking. + @param forward true to select the next item, false for the previous one +*/ +void Diagram::selectNextItem(bool forward) +{ + QList candidates; + for (auto item : items()) { + if (dynamic_cast(item) || + dynamic_cast(item) || + dynamic_cast(item)) { + candidates << item; + } + } + if (candidates.isEmpty()) + return; + + int current_index = -1; + for (int i = 0; i < candidates.size(); ++i) { + if (candidates.at(i) -> isSelected()) { + current_index = i; + break; + } + } + + int next_index; + if (current_index == -1) { + next_index = forward ? 0 : candidates.size() - 1; + } else { + next_index = forward + ? (current_index + 1) % candidates.size() + : (current_index - 1 + candidates.size()) % candidates.size(); + } + + clearSelection(); + QGraphicsItem *next_item = candidates.at(next_index); + next_item -> setSelected(true); + next_item -> ensureVisible(); +} + /** @brief Diagram::insertFolioSeqHash This class inserts a stringlist containing all diff --git a/sources/diagram.h b/sources/diagram.h index 36b346463..34422bfac 100644 --- a/sources/diagram.h +++ b/sources/diagram.h @@ -150,8 +150,11 @@ class Diagram : public QGraphicsScene void wheelEvent (QGraphicsSceneWheelEvent *event) override; void keyPressEvent (QKeyEvent *event) override; void keyReleaseEvent (QKeyEvent *) override; + bool event(QEvent *event) override; + + private: + void selectNextItem(bool forward); - public: void correctTextPos(Element* elmt); void restoreText(Element* elmt); @@ -287,6 +290,8 @@ class Diagram : public QGraphicsScene void selectAll(); void deselectAll(); void invertSelection(); + void selectAllConductors(); + void selectAllTextFields(); signals: void showDiagram (Diagram *); diff --git a/sources/diagramview.cpp b/sources/diagramview.cpp index 4b2d97563..6ee18ca60 100644 --- a/sources/diagramview.cpp +++ b/sources/diagramview.cpp @@ -702,6 +702,23 @@ void DiagramView::focusInEvent(QFocusEvent *e) { } } +/** + @brief DiagramView::focusNextPrevChild + By default, QWidget intercepts Tab/Shift+Tab to move keyboard focus to + the next/previous widget before a key press event is ever generated, + which would silently swallow the diagram's Tab-based item-selection + cycling (see Diagram::event()). Returning false here disables that + automatic focus-chain traversal for this view, so Tab/Shift+Tab reach + keyPressEvent() (and from there, the scene) as ordinary key presses + instead. + @return always false +*/ +bool DiagramView::focusNextPrevChild(bool next) +{ + Q_UNUSED(next) + return false; +} + /** @brief DiagramView::keyPressEvent Handles "key press" events. Reimplemented here to switch to visualisation diff --git a/sources/diagramview.h b/sources/diagramview.h index ae8933660..463ea552c 100644 --- a/sources/diagramview.h +++ b/sources/diagramview.h @@ -80,6 +80,7 @@ class DiagramView : public QGraphicsView void keyPressEvent(QKeyEvent *) override; void keyReleaseEvent(QKeyEvent *) override; bool event(QEvent *) override; + bool focusNextPrevChild(bool next) override; void paintEvent(QPaintEvent *event) override; void mousePressEvent(QMouseEvent *) override; void mouseMoveEvent(QMouseEvent *) override; diff --git a/sources/qetdiagrameditor.cpp b/sources/qetdiagrameditor.cpp index cf6f7d8fd..37745b3c6 100644 --- a/sources/qetdiagrameditor.cpp +++ b/sources/qetdiagrameditor.cpp @@ -688,6 +688,8 @@ void QETDiagramEditor::setUpActions() QAction *select_all = m_select_actions_group.addAction( QET::Icons::EditSelectAll, tr("Tout sélectionner") ); QAction *select_nothing = m_select_actions_group.addAction( QET::Icons::EditSelectNone, tr("Désélectionner tout") ); QAction *select_invert = m_select_actions_group.addAction( QET::Icons::EditSelectInvert, tr("Inverser la sélection") ); + QAction *select_all_conductors = m_select_actions_group.addAction( QET::Icons::Conductor, tr("Sélectionner tous les conducteurs") ); + QAction *select_all_text_fields = m_select_actions_group.addAction( QET::Icons::PartTextField, tr("Sélectionner tous les champs de texte") ); ShortcutManager::instance().registerAction(select_all, "diagrameditor.select_all", tr("Éditeur de schémas"), QKeySequence::SelectAll); ShortcutManager::instance().registerAction(select_nothing, "diagrameditor.select_nothing", tr("Éditeur de schémas"), QKeySequence::Deselect); @@ -696,10 +698,14 @@ void QETDiagramEditor::setUpActions() select_all ->setStatusTip( tr("Sélectionne tous les éléments du folio", "status bar tip") ); select_nothing->setStatusTip( tr("Désélectionne tous les éléments du folio", "status bar tip") ); select_invert ->setStatusTip( tr("Désélectionne les éléments sélectionnés et sélectionne les éléments non sélectionnés", "status bar tip") ); + select_all_conductors ->setStatusTip( tr("Sélectionne tous les conducteurs du folio, désélectionne le reste", "status bar tip") ); + select_all_text_fields->setStatusTip( tr("Sélectionne tous les champs de texte du folio, désélectionne le reste", "status bar tip") ); select_all ->setData("select_all"); select_nothing->setData("deselect"); select_invert ->setData("invert_selection"); + select_all_conductors ->setData("select_all_conductors"); + select_all_text_fields->setData("select_all_text_fields"); connect(&m_select_actions_group, &QActionGroup::triggered, this, &QETDiagramEditor::selectGroupTriggered); @@ -1584,6 +1590,10 @@ void QETDiagramEditor::selectGroupTriggered(QAction *action) diagram->deselectAll(); else if (value == "invert_selection") diagram->invertSelection(); + else if (value == "select_all_conductors") + diagram->selectAllConductors(); + else if (value == "select_all_text_fields") + diagram->selectAllTextFields(); } /**