wiRedPanda
Logic Circuit Simulator
Loading...
Searching...
No Matches
ElementTabNavigator.cpp
Go to the documentation of this file.
1// Copyright 2015 - 2026, GIBIS-UNIFESP and the wiRedPanda contributors
2// SPDX-License-Identifier: GPL-3.0-or-later
3
5
6#include <algorithm>
7
8#include <QEvent>
9#include <QKeyEvent>
10#include <QWidget>
11
13#include "App/Scene/Scene.h"
15
17 : QObject(parent)
18 , m_editor(editor)
19{
20}
21
22QVector<GraphicElement *> ElementTabNavigator::readingOrder(QVector<GraphicElement *> elements)
23{
24 // Reading order is row-major: top-to-bottom by Y (primary key), left-to-right by X within a
25 // row (secondary key). std::stable_sort is stable, so sorting by the secondary key (X) first
26 // and the primary key (Y) last leaves Y dominant while elements sharing a row keep their
27 // left-to-right X order.
28 std::stable_sort(elements.begin(), elements.end(), [](const auto &a, const auto &b) {
29 return a->pos().rx() < b->pos().rx();
30 });
31 std::stable_sort(elements.begin(), elements.end(), [](const auto &a, const auto &b) {
32 return a->pos().ry() < b->pos().ry();
33 });
34 return elements;
35}
36
37bool ElementTabNavigator::eventFilter(QObject *obj, QEvent *event)
38{
39 if (event->type() != QEvent::KeyPress) {
40 return false;
41 }
42
43 auto *keyEvent = dynamic_cast<QKeyEvent *>(event);
44
45 if (!keyEvent || (m_editor->m_elements.size() != 1)) {
46 return false;
47 }
48
49 const bool moveFwd = (keyEvent->key() == Qt::Key_Tab);
50 const bool moveBack = (keyEvent->key() == Qt::Key_Backtab);
51
52 if (!moveFwd && !moveBack) {
53 return false;
54 }
55
56 Scene *scene = m_editor->m_scene;
57
58 if (!scene) {
59 return false;
60 }
61
62 auto *elm = m_editor->m_elements.constFirst();
63 const auto elements = readingOrder(scene->visibleElements());
64
65 const qsizetype elmPos = elements.indexOf(elm);
66 if (elmPos < 0) {
67 // The focused element isn't among the visible elements (e.g. it's hidden by a
68 // visibility filter). elmPos == -1 can never be reached by the modular advance loop
69 // below, so it would spin forever; elements.at(-1) would also be out of range. Let
70 // the key event fall through to default handling instead.
71 return false;
72 }
73 const qsizetype step = moveFwd ? 1 : -1;
74 const qsizetype total = elements.size();
75
76 auto *widget = qobject_cast<QWidget *>(obj);
77 qsizetype pos = (total + elmPos + step) % total;
78
79 // Advance until we find an element whose editor widget is actually enabled
80 // (some elements don't expose the currently focused field, so we skip them).
81 for (; pos != elmPos; pos = (total + pos + step) % total) {
82 elm = elements.at(pos);
83 m_editor->setCurrentElements({elm});
84
85 if (widget->isEnabled()) {
86 break;
87 }
88 }
89
90 scene->clearSelection();
91
92 if (!widget->isEnabled()) {
93 elm = elements.at(elmPos);
94 }
95
96 elm->setSelected(true);
97 elm->ensureVisible();
98 widget->setFocus();
99 event->accept();
100 return true;
101}
ElementEditor widget for inspecting and modifying selected circuit element properties.
ElementTabNavigator event filter for Tab/Backtab cycling between scene elements.
Abstract base class for all graphical circuit elements.
Main circuit editing scene with undo/redo and user interaction.
Widget for inspecting and editing the properties of selected circuit elements.
static QVector< GraphicElement * > readingOrder(QVector< GraphicElement * > elements)
bool eventFilter(QObject *obj, QEvent *event) override
ElementTabNavigator(ElementEditor *editor, QObject *parent=nullptr)
Constructs the navigator.
Main circuit editing scene.
Definition Scene.h:56
const QVector< GraphicElement * > visibleElements() const
Returns all visible (non-hidden) graphic elements in the scene.
Definition Scene.cpp:329