wiRedPanda
Logic Circuit Simulator
Loading...
Searching...
No Matches
InlineLabelEditor.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 <QGraphicsProxyWidget>
9#include <QKeyEvent>
10#include <QLineEdit>
11
15#include "App/Scene/Commands.h"
16#include "App/Scene/Scene.h"
17
18namespace {
19
22class InlineLabelLineEdit : public QLineEdit
23{
24public:
25 explicit InlineLabelLineEdit(InlineLabelEditor *owner)
26 : m_owner(owner)
27 {
28 }
29
30protected:
31 void keyPressEvent(QKeyEvent *event) override
32 {
33 if (event->key() == Qt::Key_Escape) {
34 m_owner->cancel();
35 return;
36 }
37
38 QLineEdit::keyPressEvent(event);
39 }
40
41private:
42 InlineLabelEditor *m_owner;
43};
44
45} // namespace
46
48 : m_scene(scene)
49{
50}
51
53
55{
56 if (!element) {
57 return;
58 }
59
60 // Only one inline edit at a time; committing here also handles the common case of
61 // double-clicking a different element while this one is still focused (which itself
62 // already lost focus and committed via editingFinished by the time the double-click's
63 // second press lands, but this stays correct even if that assumption ever changes).
64 if (m_target) {
65 commit();
66 }
67
68 m_target = element;
69
70 auto *lineEdit = new InlineLabelLineEdit(this);
71 lineEdit->setText(element->label());
72
73 QObject::connect(lineEdit, &QLineEdit::returnPressed, lineEdit, [this] { commit(); });
74 QObject::connect(lineEdit, &QLineEdit::editingFinished, lineEdit, [this] { commit(); });
75
76 m_proxy = m_scene->addWidget(lineEdit);
77 m_proxy->setZValue(1000); // above every element/wire on the canvas
78
79 QRectF targetRect = element->labelSceneBoundingRect();
80 if (targetRect.isEmpty()) {
81 targetRect = element->sceneBoundingRect();
82 }
83 const qreal width = std::max(targetRect.width(), 80.0);
84 lineEdit->resize(static_cast<int>(width), lineEdit->sizeHint().height());
85 m_proxy->setPos(targetRect.topLeft());
86
87 lineEdit->setFocus();
88 lineEdit->selectAll();
89}
90
92{
93 if (!m_target) {
94 return;
95 }
96
97 auto *lineEdit = qobject_cast<QLineEdit *>(m_proxy->widget());
98 const QString newText = lineEdit ? lineEdit->text() : m_target->label();
99
100 if (newText != m_target->label()) {
101 QByteArray oldData;
102 QDataStream stream(&oldData, QIODevice::WriteOnly);
104 m_target->save(stream, {.purpose = SerializationPurpose::InMemorySnapshot});
105
106 m_target->setLabel(newText);
107 m_scene->receiveCommand(new UpdateCommand({m_target}, oldData, m_scene));
108 }
109
110 close();
111}
112
114{
115 close();
116}
117
118void InlineLabelEditor::close()
119{
120 m_target = nullptr;
121 if (m_proxy) {
122 m_proxy->deleteLater();
123 m_proxy = nullptr;
124 }
125}
All QUndoCommand subclasses and the CommandUtils helper namespace.
Abstract base class for all graphical circuit elements.
InlineLabelEditor: on-canvas inline editing of an element's label.
Main circuit editing scene with undo/redo and user interaction.
Deserialization/serialization context structs passed through load()/save() call chains.
Circuit and waveform file serialization/deserialization utilities.
Abstract base class for all graphical circuit elements in wiRedPanda.
QString label() const
Returns the user-visible label text for this element.
QRectF labelSceneBoundingRect() const
void start(GraphicElement *element)
Starts inline-editing element's label, committing any already-in-progress edit first.
void cancel()
Discards the current edit (if any) and closes the editor without changing anything.
InlineLabelEditor(Scene *scene)
Main circuit editing scene.
Definition Scene.h:56
static void writePandaHeader(QDataStream &stream)
Writes the .panda circuit file header to stream.
Undo command for property changes (label, color, frequency, appearance, etc.).
Definition Commands.h:193