Skip to content

Commit 5f6309b

Browse files
authored
make prop dialog true direct edit (scp-fs2open#7418)
1 parent d6509d8 commit 5f6309b

3 files changed

Lines changed: 75 additions & 105 deletions

File tree

qtfred/src/mission/dialogs/PropEditorDialogModel.cpp

Lines changed: 70 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -17,54 +17,11 @@ PropEditorDialogModel::PropEditorDialogModel(QObject* parent, EditorViewport* vi
1717
}
1818

1919
bool PropEditorDialogModel::apply() {
20-
if (!hasValidSelection()) {
21-
return true;
22-
}
23-
24-
if (!validateData()) {
25-
return false;
26-
}
27-
28-
for (auto obj_idx : _selectedPropObjects) {
29-
if (!query_valid_object(obj_idx) || Objects[obj_idx].type != OBJ_PROP) {
30-
continue;
31-
}
32-
33-
auto instance = Objects[obj_idx].instance;
34-
auto prp = prop_id_lookup(instance);
35-
if (prp == nullptr) {
36-
continue;
37-
}
38-
39-
if (!hasMultipleSelection()) {
40-
strcpy_s(prp->prop_name, _propName.c_str());
41-
}
42-
43-
for (size_t i = 0; i < _flagLabels.size(); ++i) {
44-
auto state = _flagState[i];
45-
if (state == Qt::PartiallyChecked) {
46-
continue;
47-
}
48-
49-
auto flag_index = _flagLabels[i].second;
50-
if (flag_index >= Num_parse_prop_flags) {
51-
continue;
52-
}
53-
54-
auto& def = Parse_prop_flags[flag_index];
55-
if (!stricmp(def.name, "no_collide")) {
56-
Objects[obj_idx].flags.set(Object::Object_Flags::Collides, state != Qt::Checked);
57-
}
58-
}
59-
}
60-
61-
_editor->missionChanged();
6220
return true;
6321
}
6422

65-
void PropEditorDialogModel::reject() {
66-
// no-op
67-
}
23+
void PropEditorDialogModel::reject() {}
24+
6825

6926
void PropEditorDialogModel::initializeData() {
7027
_flagLabels.clear();
@@ -109,40 +66,6 @@ void PropEditorDialogModel::initializeData() {
10966
_modified = false;
11067
}
11168

112-
bool PropEditorDialogModel::validateData() {
113-
_bypass_errors = false;
114-
115-
if (hasMultipleSelection()) {
116-
// Name is not editable for multi-select and only flags are applied.
117-
return true;
118-
}
119-
120-
SCP_trim(_propName);
121-
if (_propName.empty()) {
122-
showErrorDialogNoCancel("A prop name cannot be empty.");
123-
return false;
124-
}
125-
126-
std::unordered_set<int> selected_instances;
127-
for (auto obj_idx : _selectedPropObjects) {
128-
if (query_valid_object(obj_idx) && Objects[obj_idx].type == OBJ_PROP) {
129-
selected_instances.insert(Objects[obj_idx].instance);
130-
}
131-
}
132-
133-
for (size_t i = 0; i < Props.size(); ++i) {
134-
if (selected_instances.find(static_cast<int>(i)) != selected_instances.end() || !Props[i].has_value()) {
135-
continue;
136-
}
137-
138-
if (!stricmp(_propName.c_str(), Props[i].value().prop_name)) {
139-
showErrorDialogNoCancel("This prop name is already being used by another prop.");
140-
return false;
141-
}
142-
}
143-
144-
return true;
145-
}
14669

14770
void PropEditorDialogModel::showErrorDialogNoCancel(const SCP_string& message) {
14871
if (_bypass_errors) {
@@ -254,11 +177,49 @@ const SCP_string& PropEditorDialogModel::getPropName() const {
254177
return _propName;
255178
}
256179

257-
void PropEditorDialogModel::setPropName(const SCP_string& name) {
180+
bool PropEditorDialogModel::setPropName(const SCP_string& name) {
258181
if (hasMultipleSelection()) {
259-
return;
182+
return true;
183+
}
184+
185+
_bypass_errors = false;
186+
187+
SCP_string trimmed = name;
188+
SCP_trim(trimmed);
189+
190+
if (trimmed.empty()) {
191+
showErrorDialogNoCancel("A prop name cannot be empty.");
192+
return false;
193+
}
194+
195+
std::unordered_set<int> selected_instances;
196+
for (auto obj_idx : _selectedPropObjects) {
197+
if (query_valid_object(obj_idx) && Objects[obj_idx].type == OBJ_PROP) {
198+
selected_instances.insert(Objects[obj_idx].instance);
199+
}
200+
}
201+
202+
for (size_t i = 0; i < Props.size(); ++i) {
203+
if (selected_instances.find(static_cast<int>(i)) != selected_instances.end() || !Props[i].has_value()) {
204+
continue;
205+
}
206+
if (!stricmp(trimmed.c_str(), Props[i].value().prop_name)) {
207+
showErrorDialogNoCancel("This prop name is already being used by another prop.");
208+
return false;
209+
}
210+
}
211+
212+
auto obj_idx = _selectedPropObjects.front();
213+
auto prp = prop_id_lookup(Objects[obj_idx].instance);
214+
if (prp == nullptr) {
215+
return false;
260216
}
261-
modify(_propName, name);
217+
218+
strcpy_s(prp->prop_name, trimmed.c_str());
219+
_propName = trimmed;
220+
set_modified();
221+
_editor->missionChanged();
222+
return true;
262223
}
263224

264225
const SCP_vector<std::pair<SCP_string, size_t>>& PropEditorDialogModel::getFlagLabels() const {
@@ -274,11 +235,32 @@ void PropEditorDialogModel::setFlagState(size_t index, int state) {
274235
return;
275236
}
276237

277-
if (_flagState[index] != state) {
278-
_flagState[index] = state;
279-
set_modified();
280-
Q_EMIT modelChanged();
238+
if (_flagState[index] == state) {
239+
return;
240+
}
241+
242+
_flagState[index] = state;
243+
244+
if (state == Qt::PartiallyChecked) {
245+
return;
246+
}
247+
248+
auto flag_index = _flagLabels[index].second;
249+
if (flag_index >= Num_parse_prop_flags) {
250+
return;
251+
}
252+
253+
auto& def = Parse_prop_flags[flag_index];
254+
for (auto obj_idx : _selectedPropObjects) {
255+
if (!query_valid_object(obj_idx) || Objects[obj_idx].type != OBJ_PROP) {
256+
continue;
257+
}
258+
if (!stricmp(def.name, "no_collide")) {
259+
Objects[obj_idx].flags.set(Object::Object_Flags::Collides, state != Qt::Checked);
260+
}
281261
}
262+
set_modified();
263+
// Caller is responsible for triggering missionChanged() (deferred to avoid FlagListWidget re-entrancy)
282264
}
283265

284266
SCP_string PropEditorDialogModel::getLayer() const
@@ -317,10 +299,7 @@ void PropEditorDialogModel::selectNextProp() {
317299
}
318300
return;
319301
}
320-
321-
if (apply()) {
322-
selectPropFromObjectList(GET_NEXT(&Objects[_selectedPropObjects.front()]), true);
323-
}
302+
selectPropFromObjectList(GET_NEXT(&Objects[_selectedPropObjects.front()]), true);
324303
}
325304

326305
void PropEditorDialogModel::selectPreviousProp() {
@@ -330,10 +309,7 @@ void PropEditorDialogModel::selectPreviousProp() {
330309
}
331310
return;
332311
}
333-
334-
if (apply()) {
335-
selectPropFromObjectList(GET_PREV(&Objects[_selectedPropObjects.front()]), false);
336-
}
312+
selectPropFromObjectList(GET_PREV(&Objects[_selectedPropObjects.front()]), false);
337313
}
338314

339315
void PropEditorDialogModel::onSelectedObjectChanged(int) {

qtfred/src/mission/dialogs/PropEditorDialogModel.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class PropEditorDialogModel : public AbstractDialogModel {
1818
bool hasMultipleSelection() const;
1919
static bool hasAnyPropsInMission();
2020
const SCP_string& getPropName() const;
21-
void setPropName(const SCP_string& name);
21+
bool setPropName(const SCP_string& name);
2222

2323
const SCP_vector<std::pair<SCP_string, size_t>>& getFlagLabels() const;
2424
const SCP_vector<int>& getFlagState() const;
@@ -41,7 +41,6 @@ class PropEditorDialogModel : public AbstractDialogModel {
4141

4242
private: // NOLINT(readability-redundant-access-specifiers)
4343
void initializeData();
44-
bool validateData();
4544
void showErrorDialogNoCancel(const SCP_string& message);
4645
void selectPropFromObjectList(object* start, bool forward);
4746
void selectFirstPropInMission();

qtfred/src/ui/dialogs/PropEditorDialog.cpp

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,9 @@ PropEditorDialog::PropEditorDialog(FredView* parent, EditorViewport* viewport)
3535
}
3636
}
3737
}
38-
// Applying immediately can re-enter FlagListWidget while it is still processing
39-
// itemChanged, which may invalidate the underlying item/model pointers.
40-
// Queue the apply until the current signal stack unwinds.
41-
QMetaObject::invokeMethod(this, [this]() { _model->apply(); }, Qt::QueuedConnection);
38+
// Defer missionChanged to avoid re-entering FlagListWidget while it processes itemChanged,
39+
// which could invalidate the underlying item/model pointers.
40+
QMetaObject::invokeMethod(this, [this]() { _viewport->editor->missionChanged(); }, Qt::QueuedConnection);
4241
});
4342

4443
resize(QDialog::sizeHint());
@@ -90,8 +89,7 @@ void PropEditorDialog::updateUi() {
9089
}
9190

9291
void PropEditorDialog::on_propNameLineEdit_editingFinished() {
93-
_model->setPropName(ui->propNameLineEdit->text().toUtf8().constData());
94-
if (!_model->apply()) {
92+
if (!_model->setPropName(ui->propNameLineEdit->text().toUtf8().constData())) {
9593
updateUi();
9694
}
9795
}
@@ -108,9 +106,6 @@ void PropEditorDialog::on_layerCombo_currentIndexChanged(int index) {
108106
if (index < 0)
109107
return;
110108
_model->setLayer(ui->layerCombo->itemData(index).toString().toUtf8().constData());
111-
if (!_model->apply()) {
112-
updateUi();
113-
}
114109
}
115110

116111
} // namespace fso::fred::dialogs

0 commit comments

Comments
 (0)