src/dialogs/circularprimitiveeditor.cpp

changeset 1406
37fffb682d2f
parent 1395
23551de3da36
child 1407
22bc5862cb56
equal deleted inserted replaced
1405:d2bf2e59a3ef 1406:37fffb682d2f
1 /*
2 * LDForge: LDraw parts authoring CAD
3 * Copyright (C) 2013 - 2018 Teemu Piippo
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "circularprimitiveeditor.h"
20 #include "ui_circularprimitiveeditor.h"
21 #include "../primitives.h"
22
23 // offsetof doesn't work if this doesn't hold true
24 static_assert(std::is_standard_layout<Ui_CircularPrimitiveEditor>::value, "UI type is not of standard layout");
25
26 // Contains offsets to radio buttons and the choice they represent.
27 static const struct
28 {
29 std::ptrdiff_t offset;
30 PrimitiveModel::Type primitiveType;
31
32 QRadioButton* resolve(Ui_CircularPrimitiveEditor& ui) const
33 {
34 // Find the radio button by pointer arithmetic
35 return *reinterpret_cast<QRadioButton**>(reinterpret_cast<char*>(&ui) + offset);
36 }
37 } radioButtonMap[] = {
38 #define MAP_RADIO_BUTTON(widget, type) { offsetof(Ui_CircularPrimitiveEditor, widget), PrimitiveModel::type }
39 MAP_RADIO_BUTTON(circle, Circle),
40 MAP_RADIO_BUTTON(cylinder, Cylinder),
41 MAP_RADIO_BUTTON(disc, Disc),
42 MAP_RADIO_BUTTON(discNegative, DiscNegative),
43 #undef MAP_RADIO_BUTTON
44 };
45
46 /*
47 * Constructs a new circular primitive editor and sets up connections.
48 */
49 CircularPrimitiveEditor::CircularPrimitiveEditor(LDCircularPrimitive* primitive, QWidget* parent) :
50 QDialog {parent},
51 ui {*new Ui_CircularPrimitiveEditor},
52 primitive {primitive}
53 {
54 ui.setupUi(this);
55
56 // Set the initial values of the dialog
57 updateWidgets();
58
59 if (primitive)
60 {
61 // Store the original state of the object. If the user presses "Reset" then the object is restored
62 // from this archive.
63 Serializer serializer {originalState, Serializer::Store};
64 primitive->serialize(serializer);
65 }
66
67 for (const auto& mapping : ::radioButtonMap)
68 {
69 QRadioButton* button = mapping.resolve(ui);
70
71 // If the radio button gets checked, update the type of the circular primitive.
72 connect(
73 button,
74 &QRadioButton::toggled,
75 [&](bool checked)
76 {
77 if (checked and this->primitive)
78 this->primitive->setPrimitiveType(mapping.primitiveType);
79 }
80 );
81 }
82
83 // Connect various widgets so that changing them changes the primitive object.
84 connect(
85 ui.segments,
86 qOverload<int>(&QSpinBox::valueChanged),
87 [&](int newSegments)
88 {
89 if (this->primitive)
90 this->primitive->setSegments(newSegments);
91 }
92 );
93 connect(
94 ui.divisions,
95 &QComboBox::currentTextChanged,
96 [&](const QString& newDivisions)
97 {
98 if (this->primitive)
99 this->primitive->setDivisions(newDivisions.toInt());
100 }
101 );
102 connect(
103 ui.color,
104 &ColorButton::colorChanged,
105 [&](LDColor newColor)
106 {
107 if (this->primitive)
108 this->primitive->setColor(newColor);
109 }
110 );
111 connect(
112 ui.matrix,
113 &MatrixEditor::matrixChanged,
114 [&](const QMatrix4x4& newMatrix)
115 {
116 if (this->primitive)
117 this->primitive->setTransformationMatrix(newMatrix);
118 }
119 );
120 // Connect the reset button, "reset button" here meaning any button with the reset role.
121 connect(
122 ui.buttonBox,
123 &QDialogButtonBox::clicked,
124 [&](QAbstractButton* button)
125 {
126 if (ui.buttonBox->buttonRole(button) == QDialogButtonBox::ResetRole)
127 reset();
128 }
129 );
130
131 if (this->primitive)
132 {
133 // If the primitive is changed by some other thing (e.g. by resetting it), update the widgets.
134 connect(this->primitive, &LDObject::modified, this, &CircularPrimitiveEditor::updateWidgets);
135
136 // If the object is deleted, then destroy the dialog.
137 connect(this->primitive, &LDObject::destroyed, this, &QDialog::reject);
138 }
139 }
140
141 /*
142 * Frees the user interface memory after the dialog is destroyed.
143 */
144 CircularPrimitiveEditor::~CircularPrimitiveEditor()
145 {
146 delete &ui;
147 }
148
149 /*
150 * Updates the widgets of the editor to reflect the properites of the object being modified.
151 */
152 void CircularPrimitiveEditor::updateWidgets()
153 {
154 setEnabled(primitive != nullptr);
155
156 if (primitive)
157 {
158 for (const auto& mapping : ::radioButtonMap)
159 {
160 // Choose the correct radio button
161 QRadioButton* button = mapping.resolve(ui);
162 withSignalsBlocked(button, [&]()
163 {
164 button->setChecked(primitive->primitiveType() == mapping.primitiveType);
165 });
166 }
167
168 // Set the values of the form.
169 withSignalsBlocked(ui.segments, [&](){ ui.segments->setValue(primitive->segments()); });
170 withSignalsBlocked(ui.divisions, [&]()
171 {
172 ui.divisions->setCurrentText(QString::number(primitive->divisions()));
173 });
174 withSignalsBlocked(ui.color, [&](){ ui.color->setColor(primitive->color()); });
175 withSignalsBlocked(ui.matrix, [&](){ ui.matrix->setMatrix(primitive->transformationMatrix()); });
176 }
177 }
178
179 /*
180 * Resets the object being modified. The object will emit a signal that is connected to updateWidgets.
181 */
182 void CircularPrimitiveEditor::reset()
183 {
184 if (primitive)
185 primitive->restore(originalState); // Restoring does not change 'originalState'
186 }

mercurial