diff --git a/qtfred/src/mission/dialogs/ObjectOrientEditorDialogModel.cpp b/qtfred/src/mission/dialogs/ObjectOrientEditorDialogModel.cpp index e879a5d2b42..ac4c3658f81 100644 --- a/qtfred/src/mission/dialogs/ObjectOrientEditorDialogModel.cpp +++ b/qtfred/src/mission/dialogs/ObjectOrientEditorDialogModel.cpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include namespace fso::fred::dialogs { @@ -44,8 +46,17 @@ void ObjectOrientEditorDialogModel::initializeData() _pointToObjectList.emplace_back(ObjectEntry(text, OBJ_INDEX(ptr))); break; } + case OBJ_JUMP_NODE: { + CJumpNode* jnp = jumpnode_get_by_objnum(OBJ_INDEX(ptr)); + if (jnp) + _pointToObjectList.emplace_back(ObjectEntry(jnp->GetName(), OBJ_INDEX(ptr))); + break; + } + case OBJ_PROP: + if (Props[ptr->instance].has_value()) + _pointToObjectList.emplace_back(ObjectEntry(Props[ptr->instance]->prop_name, OBJ_INDEX(ptr))); + break; case OBJ_POINT: - case OBJ_JUMP_NODE: break; default: Assertion(false, "Unknown object type in Object Orient Dialog!"); // unknown object type diff --git a/qtfred/src/mission/dialogs/ShipEditor/ShipEditorDialogModel.cpp b/qtfred/src/mission/dialogs/ShipEditor/ShipEditorDialogModel.cpp index 2d0dbcf11e9..79e7668f714 100644 --- a/qtfred/src/mission/dialogs/ShipEditor/ShipEditorDialogModel.cpp +++ b/qtfred/src/mission/dialogs/ShipEditor/ShipEditorDialogModel.cpp @@ -31,21 +31,12 @@ ShipEditorDialogModel::ShipEditorDialogModel(QObject* parent, EditorViewport* vi int ShipEditorDialogModel::tristate_set(int val, int cur_state) { - if (val) { - if (!cur_state) { - return Qt::PartiallyChecked; - } - } else { - if (cur_state) { - return Qt::PartiallyChecked; - } - } - if (cur_state == 1) { - - return Qt::Checked; - } else { - return Qt::Unchecked; - } + if (cur_state == Qt::PartiallyChecked) + return Qt::PartiallyChecked; + bool cur_bool = (cur_state == Qt::Checked); + if (static_cast(val) != cur_bool) + return Qt::PartiallyChecked; + return cur_state; } int ShipEditorDialogModel::getSingleShip() const { @@ -290,7 +281,7 @@ void ShipEditorDialogModel::initializeData() if (base_player >= 0) { _m_ship_class = Ships[i].ship_info_index; _m_team = Ships[i].team; - pship = (objp->type == OBJ_START) ? 1 : 0; + pship = (objp->type == OBJ_START) ? Qt::Checked : Qt::Unchecked; base_player = -1; } else { if (Ships[i].ship_info_index != _m_ship_class) { diff --git a/qtfred/src/mission/dialogs/ShipEditor/ShipFlagsDialogModel.cpp b/qtfred/src/mission/dialogs/ShipEditor/ShipFlagsDialogModel.cpp index 18a535592ea..f2055ec55c3 100644 --- a/qtfred/src/mission/dialogs/ShipEditor/ShipFlagsDialogModel.cpp +++ b/qtfred/src/mission/dialogs/ShipEditor/ShipFlagsDialogModel.cpp @@ -6,23 +6,22 @@ #include namespace fso::fred::dialogs { + +// Flags that are not applicable to player start ships and should be hidden/skipped for them. +// Add entries here to extend the list. +static const Ship::Ship_Flags player_start_hidden_flags[] = { + Ship::Ship_Flags::Reinforcement, + Ship::Ship_Flags::Kill_before_mission, +}; int ShipFlagsDialogModel::tristate_set(int val, int cur_state) { - if (val) { - if (!cur_state) { - return CheckState::PartiallyChecked; - } - } else { - if (cur_state) { - return CheckState::PartiallyChecked; - } - } - if (cur_state == 1) { - - return CheckState::Checked; - } else { - return CheckState::Unchecked; - } + // cur_state uses Qt::CheckState encoding (0=Unchecked, 1=PartiallyChecked, 2=Checked) + if (cur_state == Qt::PartiallyChecked) + return Qt::PartiallyChecked; + bool cur_bool = (cur_state == Qt::Checked); + if (static_cast(val) != cur_bool) + return Qt::PartiallyChecked; + return cur_state; } std::pair* ShipFlagsDialogModel::getFlag(const SCP_string& flag_name) { @@ -81,12 +80,26 @@ void ShipFlagsDialogModel::update_ship(const int shipnum) ship* shipp = &Ships[shipnum]; object* objp = &Objects[shipp->objnum]; for (const auto& [name, checked] : flags) { + // PartiallyChecked means mixed selection — leave each ship's flag as-is + if (checked == Qt::PartiallyChecked) + continue; + const bool set = (checked == Qt::Checked); for (size_t i = 0; i < Num_Parse_ship_flags; ++i) { if (!stricmp(name.c_str(), Parse_ship_flags[i].name)) { + + // Skip flags that aren't applicable to player start ships, even if they were shown and edited in multi edit mode + if (objp->type == OBJ_START) { + bool hidden = false; + for (const auto& hf : player_start_hidden_flags) { + if (Parse_ship_flags[i].def == hf) { hidden = true; break; } + } + if (hidden) continue; + } + if (Parse_ship_flags[i].def == Ship::Ship_Flags::Reinforcement) { - _editor->set_reinforcement(shipp->ship_name, checked); + _editor->set_reinforcement(shipp->ship_name, set); } else { - if (checked) { + if (set) { shipp->flags.set(Parse_ship_flags[i].def); } else { shipp->flags.remove(Parse_ship_flags[i].def); @@ -97,7 +110,7 @@ void ShipFlagsDialogModel::update_ship(const int shipnum) } for (size_t i = 0; i < Num_Parse_ship_ai_flags; ++i) { if (!stricmp(name.c_str(), Parse_ship_ai_flags[i].name)) { - if (checked) { + if (set) { Ai_info[shipp->ai_index].ai_flags.set(Parse_ship_ai_flags[i].def); } else { Ai_info[shipp->ai_index].ai_flags.remove(Parse_ship_ai_flags[i].def); @@ -108,13 +121,13 @@ void ShipFlagsDialogModel::update_ship(const int shipnum) for (size_t i = 0; i < Num_Parse_ship_object_flags; ++i) { if (!stricmp(name.c_str(), Parse_ship_object_flags[i].name)) { if (Parse_ship_object_flags[i].def == Object::Object_Flags::Collides) { - if (checked) { + if (set) { objp->flags.remove(Parse_ship_object_flags[i].def); } else { objp->flags.set(Parse_ship_object_flags[i].def); } } else { - if (checked) { + if (set) { objp->flags.set(Parse_ship_object_flags[i].def); } else { objp->flags.remove(Parse_ship_object_flags[i].def); @@ -220,6 +233,21 @@ void ShipFlagsDialogModel::initializeData() first = 1; + bool all_player_ships = false; + bool any_marked = false; + for (objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp)) { + if (((objp->type == OBJ_START) || (objp->type == OBJ_SHIP)) && objp->flags[Object::Object_Flags::Marked]) { + if (!any_marked) { + all_player_ships = true; + any_marked = true; + } + if (objp->type != OBJ_START) { + all_player_ships = false; + break; + } + } + } + objp = GET_FIRST(&obj_used_list); while (objp != END_OF_LIST(&obj_used_list)) { if ((objp->type == OBJ_START) || (objp->type == OBJ_SHIP)) { @@ -246,13 +274,20 @@ void ShipFlagsDialogModel::initializeData() flagDef.def == Ship::Ship_Flags::Force_shields_on) { continue; } + if (all_player_ships) { + bool hidden = false; + for (const auto& hf : player_start_hidden_flags) { + if (flagDef.def == hf) { hidden = true; break; } + } + if (hidden) continue; + } bool checked = shipp->flags[flagDef.def]; - flags.emplace_back(flagDef.name, checked); + flags.emplace_back(flagDef.name, checked ? Qt::Checked : Qt::Unchecked); } for (size_t i = 0; i < Num_Parse_ship_ai_flags; i++) { auto flagDef = Parse_ship_ai_flags[i]; bool checked = Ai_info[shipp->ai_index].ai_flags[flagDef.def]; - flags.emplace_back(flagDef.name, checked); + flags.emplace_back(flagDef.name, checked ? Qt::Checked : Qt::Unchecked); } for (size_t i = 0; i < Num_Parse_ship_object_flags; i++) { auto flagDef = Parse_ship_object_flags[i]; @@ -262,8 +297,9 @@ void ShipFlagsDialogModel::initializeData() } else { checked = objp->flags[flagDef.def]; } - flags.emplace_back(flagDef.name, checked); + flags.emplace_back(flagDef.name, checked ? Qt::Checked : Qt::Unchecked); } + first = 0; } else { for (size_t i = 0; i < Num_Parse_ship_flags; i++) { auto flagDef = Parse_ship_flags[i]; @@ -281,6 +317,13 @@ void ShipFlagsDialogModel::initializeData() flagDef.def == Ship::Ship_Flags::Force_shields_on) { continue; } + if (all_player_ships) { + bool hidden = false; + for (const auto& hf : player_start_hidden_flags) { + if (flagDef.def == hf) { hidden = true; break; } + } + if (hidden) continue; + } bool checked = shipp->flags[flagDef.def]; getFlag(flagDef.name)->second = tristate_set(checked, getFlag(flagDef.name)->second); } diff --git a/qtfred/src/mission/dialogs/ShipEditor/ShipInitialStatusDialogModel.cpp b/qtfred/src/mission/dialogs/ShipEditor/ShipInitialStatusDialogModel.cpp index c37ffa0feb0..e0312d7fcbd 100644 --- a/qtfred/src/mission/dialogs/ShipEditor/ShipInitialStatusDialogModel.cpp +++ b/qtfred/src/mission/dialogs/ShipEditor/ShipInitialStatusDialogModel.cpp @@ -120,6 +120,8 @@ void ShipInitialStatusDialogModel::initializeData(bool multi) Assert(m_ship >= 0); } + m_move_ships_when_undocking = _viewport->Move_ships_when_undocking; + // initialize dockpoint stuff if (!m_multi_edit) { num_dock_points = model_get_num_dock_points(Ship_info[Ships[m_ship].ship_info_index].model_num); @@ -142,48 +144,48 @@ void ShipInitialStatusDialogModel::initializeData(bool multi) m_hull = static_cast(Objects[_editor->currentObject].hull_strength); guardian_threshold = Ships[m_ship].ship_guardian_threshold; if (Objects[_editor->currentObject].flags[Object::Object_Flags::No_shields]) - m_has_shields = 0; + m_has_shields = Qt::Unchecked; else - m_has_shields = 1; + m_has_shields = Qt::Checked; if (Ships[m_ship].flags[Ship::Ship_Flags::Force_shields_on]) - m_force_shields = 1; + m_force_shields = Qt::Checked; else - m_force_shields = 0; + m_force_shields = Qt::Unchecked; if (Ships[m_ship].flags[Ship::Ship_Flags::Ship_locked]) - m_ship_locked = 1; + m_ship_locked = Qt::Checked; else - m_ship_locked = 0; + m_ship_locked = Qt::Unchecked; if (Ships[m_ship].flags[Ship::Ship_Flags::Weapons_locked]) - m_weapons_locked = 1; + m_weapons_locked = Qt::Checked; else - m_weapons_locked = 0; + m_weapons_locked = Qt::Unchecked; // Lock primaries if (Ships[m_ship].flags[Ship::Ship_Flags::Primaries_locked]) { - m_primaries_locked = 1; + m_primaries_locked = Qt::Checked; } else { - m_primaries_locked = 0; + m_primaries_locked = Qt::Unchecked; } // Lock secondaries if (Ships[m_ship].flags[Ship::Ship_Flags::Secondaries_locked]) { - m_secondaries_locked = 1; + m_secondaries_locked = Qt::Checked; } else { - m_secondaries_locked = 0; + m_secondaries_locked = Qt::Unchecked; } // Lock turrets if (Ships[m_ship].flags[Ship::Ship_Flags::Lock_all_turrets_initially]) { - m_turrets_locked = 1; + m_turrets_locked = Qt::Checked; } else { - m_turrets_locked = 0; + m_turrets_locked = Qt::Unchecked; } if (Ships[m_ship].flags[Ship::Ship_Flags::Afterburner_locked]) { - m_afterburner_locked = 1; + m_afterburner_locked = Qt::Checked; } else { - m_afterburner_locked = 0; + m_afterburner_locked = Qt::Unchecked; } if (m_multi_edit) { @@ -199,11 +201,11 @@ void ShipInitialStatusDialogModel::initializeData(bool multi) hflag = 1; if (objp->flags[Object::Object_Flags::No_shields]) { if (m_has_shields) - m_has_shields = 1; + m_has_shields = Qt::PartiallyChecked; } else { if (!m_has_shields) - m_has_shields = 1; + m_has_shields = Qt::PartiallyChecked; } Assert((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)); @@ -337,7 +339,7 @@ void ShipInitialStatusDialogModel::undock(object* objp1, object* objp2) ship_num = get_ship_from_obj(OBJ_INDEX(objp1)); other_ship_num = get_ship_from_obj(OBJ_INDEX(objp2)); - if (_viewport->Move_ships_when_undocking) { + if (m_move_ships_when_undocking) { if (ship_class_compare(Ships[ship_num].ship_info_index, Ships[other_ship_num].ship_info_index) <= 0) { vm_vec_scale_add2(&objp2->pos, &v, @@ -592,9 +594,9 @@ bool ShipInitialStatusDialogModel::apply() modify(objp->hull_strength, (float)m_hull); - if (m_has_shields == 1) { + if (m_has_shields == Qt::Checked) { objp->flags.remove(Object::Object_Flags::No_shields); - } else if (m_has_shields == 0) { + } else if (m_has_shields == Qt::Unchecked) { objp->flags.set(Object::Object_Flags::No_shields); } auto shipp = &Ships[get_ship_from_obj(objp)]; @@ -637,6 +639,15 @@ bool ShipInitialStatusDialogModel::apply() void ShipInitialStatusDialogModel::reject() {} +bool ShipInitialStatusDialogModel::getMoveShipsWhenUndocking() const +{ + return m_move_ships_when_undocking; +} +void ShipInitialStatusDialogModel::setMoveShipsWhenUndocking(bool value) +{ + modify(m_move_ships_when_undocking, value); +} + void ShipInitialStatusDialogModel::setVelocity(const int value) { modify(m_velocity, value); diff --git a/qtfred/src/mission/dialogs/ShipEditor/ShipInitialStatusDialogModel.h b/qtfred/src/mission/dialogs/ShipEditor/ShipInitialStatusDialogModel.h index 014342f9976..f62ec91e214 100644 --- a/qtfred/src/mission/dialogs/ShipEditor/ShipInitialStatusDialogModel.h +++ b/qtfred/src/mission/dialogs/ShipEditor/ShipInitialStatusDialogModel.h @@ -62,6 +62,7 @@ class ShipInitialStatusDialogModel : public AbstractDialogModel { dock_evaluate_tree(object* objp, dock_function_info* infop, void (*function)(object*), ubyte* visited_bitstring); bool m_multi_edit; bool m_use_teams = false; + bool m_move_ships_when_undocking = true; public: ShipInitialStatusDialogModel(QObject* parent, EditorViewport* viewport, bool multi); @@ -132,6 +133,9 @@ class ShipInitialStatusDialogModel : public AbstractDialogModel { int getGuardian() const; void setGuardian(int); + + bool getMoveShipsWhenUndocking() const; + void setMoveShipsWhenUndocking(bool); }; /** diff --git a/qtfred/src/ui/FredView.cpp b/qtfred/src/ui/FredView.cpp index a7705b15f0e..ecf7835bacd 100644 --- a/qtfred/src/ui/FredView.cpp +++ b/qtfred/src/ui/FredView.cpp @@ -816,11 +816,15 @@ void FredView::onUpdateContextToolbar() { // OBJ_START is treated as OBJ_SHIP throughout. int multiCommonType = -1; int multiSharedWing = -1; + waypoint_list* multiSharedWaypointList = nullptr; if (numMarked > 1) { int firstType = -1; bool allSameType = true; int sharedWingTmp = -2; // -2 = uninitialized bool allSameWing = true; + waypoint_list* sharedWpListTmp = nullptr; + bool firstWpListSet = false; + bool allSameWpList = true; for (object* p = GET_FIRST(&obj_used_list); p != END_OF_LIST(&obj_used_list); p = GET_NEXT(p)) { if (!p->flags[Object::Object_Flags::Marked]) continue; int t = (p->type == OBJ_START) ? OBJ_SHIP : p->type; @@ -837,11 +841,23 @@ void FredView::onUpdateContextToolbar() { allSameWing = false; } } + if (t == OBJ_WAYPOINT) { + waypoint* wp = find_waypoint_with_instance(p->instance); + waypoint_list* wl = wp ? wp->get_parent_list() : nullptr; + if (!firstWpListSet) { + sharedWpListTmp = wl; + firstWpListSet = true; + } else if (wl != sharedWpListTmp) { + allSameWpList = false; + } + } } if (allSameType && firstType != -1) multiCommonType = firstType; if (multiCommonType == OBJ_SHIP && allSameWing && sharedWingTmp >= 0 && sharedWingTmp < MAX_WINGS) multiSharedWing = sharedWingTmp; + if (multiCommonType == OBJ_WAYPOINT && allSameWpList && firstWpListSet) + multiSharedWaypointList = sharedWpListTmp; } // Unified "effective" selection properties for single and multi @@ -871,6 +887,8 @@ void FredView::onUpdateContextToolbar() { label = parts.join(", ") + tr(" selected"); if (effectiveInWing) label += tr(" | Wing: %1").arg(QString::fromUtf8(Wings[effectiveWingNum].name)); + if (multiSharedWaypointList != nullptr) + label += tr(" | List: %1").arg(QString::fromUtf8(multiSharedWaypointList->get_name())); } else if (isShip) { int si = Ships[Objects[curObj].instance].ship_info_index; label = tr("Ship: %1 [%2]") @@ -892,18 +910,20 @@ void FredView::onUpdateContextToolbar() { _contextLabel->setText(label); // Only rebuild buttons when effective selection state changes. - const bool needsRebuild = (curObj != _ctxCachedObj || - numMarked != _ctxCachedMarked || - effectiveType != _ctxCachedObjType || - effectiveInWing != _ctxCachedInWing || - multiSharedWing != _ctxCachedSharedWing); + const bool needsRebuild = (curObj != _ctxCachedObj || + numMarked != _ctxCachedMarked || + effectiveType != _ctxCachedObjType || + effectiveInWing != _ctxCachedInWing || + multiSharedWing != _ctxCachedSharedWing || + multiSharedWaypointList != _ctxCachedSharedWaypointList); if (!needsRebuild) return; - _ctxCachedObj = curObj; - _ctxCachedMarked = numMarked; - _ctxCachedObjType = effectiveType; - _ctxCachedInWing = effectiveInWing; - _ctxCachedSharedWing = multiSharedWing; + _ctxCachedObj = curObj; + _ctxCachedMarked = numMarked; + _ctxCachedObjType = effectiveType; + _ctxCachedInWing = effectiveInWing; + _ctxCachedSharedWing = multiSharedWing; + _ctxCachedSharedWaypointList = multiSharedWaypointList; // Tear down previous dynamic buttons, deleting them to avoid leaks. // Toolbar layout: [0]=label widget-action, [1]=separator, [2..]=dynamic @@ -927,7 +947,6 @@ void FredView::onUpdateContextToolbar() { if (numMarked <= 1) addBtn(tr("Rename"), &FredView::quickRenameCurrentObject); addBtn(tr("Edit Ship"), &FredView::on_actionShips_triggered); - addBtn(tr("Position/Orientation"), &FredView::on_actionObject_Orientation_triggered); if (effectiveInWing) { _contextToolBar->addSeparator(); addBtn(tr("Edit Wing"), &FredView::on_actionWings_triggered); @@ -938,19 +957,17 @@ void FredView::onUpdateContextToolbar() { }); _contextToolBar->addAction(selWingAct); } - } else if (numMarked <= 1 && effectiveType == OBJ_WAYPOINT) { + } else if (effectiveType == OBJ_WAYPOINT && (numMarked <= 1 || multiSharedWaypointList != nullptr)) { addBtn(tr("Edit Waypoint Path"), &FredView::on_actionWaypoint_Paths_triggered); - addBtn(tr("Position/Orientation"), &FredView::on_actionObject_Orientation_triggered); } else if (numMarked <= 1 && effectiveType == OBJ_JUMP_NODE) { addBtn(tr("Edit Jump Node"), &FredView::on_actionJump_Nodes_triggered); - addBtn(tr("Position/Orientation"), &FredView::on_actionObject_Orientation_triggered); - } else if (numMarked <= 1 && effectiveType == OBJ_PROP) { + } else if (effectiveType == OBJ_PROP) { addBtn(tr("Edit Prop"), &FredView::on_actionProps_triggered); - addBtn(tr("Position/Orientation"), &FredView::on_actionObject_Orientation_triggered); } if (anythingSelected) { _contextToolBar->addSeparator(); + addBtn(tr("Position/Orientation"), &FredView::on_actionObject_Orientation_triggered); if (numMarked <= 1) addBtn(tr("Clone"), &FredView::on_actionClone_Marked_Objects_triggered); addBtn(tr("Delete"), &FredView::on_actionDelete_triggered); diff --git a/qtfred/src/ui/FredView.h b/qtfred/src/ui/FredView.h index 46a89823170..695a2cc5913 100644 --- a/qtfred/src/ui/FredView.h +++ b/qtfred/src/ui/FredView.h @@ -20,6 +20,8 @@ #include #include +class waypoint_list; + namespace fso { namespace fred { @@ -298,11 +300,12 @@ class FredView: public QMainWindow, public IDialogProvider { QLabel* _contextLabel = nullptr; // Cached selection state... buttons only rebuild when these change - int _ctxCachedObj = -2; // -2 = uninitialized - int _ctxCachedMarked = -1; - int _ctxCachedObjType = -1; // single: actual type; multi: common type (-1=mixed) - bool _ctxCachedInWing = false; - int _ctxCachedSharedWing = -2; // multi-select: shared wing index (-1=none, -2=N/A) + int _ctxCachedObj = -2; // -2 = uninitialized + int _ctxCachedMarked = -1; + int _ctxCachedObjType = -1; // single: actual type; multi: common type (-1=mixed) + bool _ctxCachedInWing = false; + int _ctxCachedSharedWing = -2; // multi-select: shared wing index (-1=none, -2=N/A) + waypoint_list* _ctxCachedSharedWaypointList = nullptr; QToolBar* _transformToolBar = nullptr; QLabel* _transformLabel = nullptr; diff --git a/qtfred/src/ui/dialogs/PropEditorDialog.cpp b/qtfred/src/ui/dialogs/PropEditorDialog.cpp index d5c6b7d1e56..d3cab36b7e3 100644 --- a/qtfred/src/ui/dialogs/PropEditorDialog.cpp +++ b/qtfred/src/ui/dialogs/PropEditorDialog.cpp @@ -24,20 +24,22 @@ PropEditorDialog::PropEditorDialog(FredView* parent, EditorViewport* viewport) updateUi(); }); - connect(ui->propFlagsListWidget, &fso::fred::FlagListWidget::flagToggled, this, [this](const QString& name, int checked) { - const auto& labels = _model->getFlagLabels(); - for (size_t i = 0; i < labels.size(); ++i) { - if (name == QString::fromStdString(labels[i].first)) { - _model->setFlagState(i, checked); - break; + connect(ui->propFlagsListWidget, &fso::fred::FlagListWidget::flagsChanged, this, + [this](const QVector>& snapshot) { + const auto& labels = _model->getFlagLabels(); + for (const auto& [name, state] : snapshot) { + for (size_t i = 0; i < labels.size(); ++i) { + if (name == QString::fromStdString(labels[i].first)) { + _model->setFlagState(i, state); + break; + } + } } - } - - // Applying immediately can re-enter FlagListWidget while it is still processing - // itemChanged, which may invalidate the underlying item/model pointers. - // Queue the apply until the current signal stack unwinds. - QMetaObject::invokeMethod(this, [this]() { _model->apply(); }, Qt::QueuedConnection); - }); + // Applying immediately can re-enter FlagListWidget while it is still processing + // itemChanged, which may invalidate the underlying item/model pointers. + // Queue the apply until the current signal stack unwinds. + QMetaObject::invokeMethod(this, [this]() { _model->apply(); }, Qt::QueuedConnection); + }); resize(QDialog::sizeHint()); } diff --git a/qtfred/src/ui/dialogs/ShipEditor/ShipEditorDialog.cpp b/qtfred/src/ui/dialogs/ShipEditor/ShipEditorDialog.cpp index 1c7df41e655..efaf834739e 100644 --- a/qtfred/src/ui/dialogs/ShipEditor/ShipEditorDialog.cpp +++ b/qtfred/src/ui/dialogs/ShipEditor/ShipEditorDialog.cpp @@ -257,6 +257,7 @@ void ShipEditorDialog::updateColumnTwo(bool overwrite) ui->playerShipCheckBox->setChecked(_model->getPlayer()); ui->respawnSpinBox->setValue(_model->getRespawn()); + ui->hotkeyCombo->setCurrentIndex(_model->getHotkey()); } } void ShipEditorDialog::updateArrival(bool overwrite) @@ -337,7 +338,7 @@ void ShipEditorDialog::updateArrival(bool overwrite) ui->arrivalTree->clear_tree(""); } - ui->noArrivalWarpCheckBox->setChecked(_model->getNoArrivalWarp()); + ui->noArrivalWarpCheckBox->setCheckState(Qt::CheckState(_model->getNoArrivalWarp())); } } void ShipEditorDialog::updateDeparture(bool overwrite) @@ -394,7 +395,7 @@ void ShipEditorDialog::updateDeparture(bool overwrite) ui->departureTree->clear_tree(""); } - ui->noDepartureWarpCheckBox->setChecked(_model->getNoDepartureWarp()); + ui->noDepartureWarpCheckBox->setCheckState(Qt::CheckState(_model->getNoDepartureWarp())); ui->updateDepartureCueCheckBox->setChecked(_model->getDepartureCue()); } @@ -814,8 +815,7 @@ void ShipEditorDialog::on_layerCombo_currentIndexChanged(int index) } void ShipEditorDialog::on_hotkeyCombo_currentIndexChanged(int index) { - auto hotkeyIdx = ui->hotkeyCombo->itemData(index).toInt(); - _model->setHotkey(hotkeyIdx); + _model->setHotkey(index); } void ShipEditorDialog::on_personaCombo_currentIndexChanged(int index) { @@ -860,9 +860,11 @@ void ShipEditorDialog::on_updateArrivalCueCheckBox_toggled(bool value) { _model->setArrivalCue(value); } -void ShipEditorDialog::on_noArrivalWarpCheckBox_toggled(bool value) +void ShipEditorDialog::on_noArrivalWarpCheckBox_stateChanged(int state) { - _model->setNoArrivalWarp(value); + if (state == Qt::PartiallyChecked) + return; + _model->setNoArrivalWarp(state); } void ShipEditorDialog::on_arrivalTree_rootNodeFormulaChanged(int old, int node) { @@ -906,8 +908,10 @@ void ShipEditorDialog::on_departureTree_miniHelpChanged(const QString& help) { ui->HelpTitle->setText(help); } -void ShipEditorDialog::on_noDepartureWarpCheckBox_toggled(bool value) +void ShipEditorDialog::on_noDepartureWarpCheckBox_stateChanged(int state) { - _model->setNoDepartureWarp(value); + if (state == Qt::PartiallyChecked) + return; + _model->setNoDepartureWarp(state); } } // namespace fso::fred::dialogs \ No newline at end of file diff --git a/qtfred/src/ui/dialogs/ShipEditor/ShipEditorDialog.h b/qtfred/src/ui/dialogs/ShipEditor/ShipEditorDialog.h index 34344efb2f4..2f35675794d 100644 --- a/qtfred/src/ui/dialogs/ShipEditor/ShipEditorDialog.h +++ b/qtfred/src/ui/dialogs/ShipEditor/ShipEditorDialog.h @@ -103,7 +103,7 @@ class ShipEditorDialog : public QDialog, public SexpTreeEditorInterface { void on_arrivalDistanceEdit_valueChanged(int); void on_arrivalDelaySpinBox_valueChanged(int); void on_updateArrivalCueCheckBox_toggled(bool); - void on_noArrivalWarpCheckBox_toggled(bool); + void on_noArrivalWarpCheckBox_stateChanged(int); void on_arrivalTree_rootNodeFormulaChanged(int, int); void on_arrivalTree_helpChanged(const QString&); void on_arrivalTree_miniHelpChanged(const QString&); @@ -116,7 +116,7 @@ class ShipEditorDialog : public QDialog, public SexpTreeEditorInterface { void on_departureTree_rootNodeFormulaChanged(int, int); void on_departureTree_helpChanged(const QString&); void on_departureTree_miniHelpChanged(const QString&); - void on_noDepartureWarpCheckBox_toggled(bool); + void on_noDepartureWarpCheckBox_stateChanged(int); private: // NOLINT(readability-redundant-access-specifiers) std::unique_ptr ui; std::unique_ptr _model; diff --git a/qtfred/src/ui/dialogs/ShipEditor/ShipFlagsDialog.cpp b/qtfred/src/ui/dialogs/ShipEditor/ShipFlagsDialog.cpp index f50c72af6c7..1c98bb1ff15 100644 --- a/qtfred/src/ui/dialogs/ShipEditor/ShipFlagsDialog.cpp +++ b/qtfred/src/ui/dialogs/ShipEditor/ShipFlagsDialog.cpp @@ -16,10 +16,13 @@ ShipFlagsDialog::ShipFlagsDialog(QWidget* parent, EditorViewport* viewport) ui->setupUi(this); setAttribute(Qt::WA_AlwaysShowToolTips); - connect(ui->flagList, &fso::fred::FlagListWidget::flagToggled, this, [this](const QString& name, bool checked) { - _model->setFlag(name.toUtf8().constData(), checked); - updateUI(); - }); + connect(ui->flagList, &fso::fred::FlagListWidget::flagsChanged, this, + [this](const QVector>& snapshot) { + for (const auto& [name, state] : snapshot) { + _model->setFlag(name.toUtf8().constData(), state); + } + updateUI(); + }); const auto& flags = _model->getFlagsList(); diff --git a/qtfred/src/ui/dialogs/ShipEditor/ShipInitialStatusDialog.cpp b/qtfred/src/ui/dialogs/ShipEditor/ShipInitialStatusDialog.cpp index 4ec00da84f6..cc87db32013 100644 --- a/qtfred/src/ui/dialogs/ShipEditor/ShipInitialStatusDialog.cpp +++ b/qtfred/src/ui/dialogs/ShipEditor/ShipInitialStatusDialog.cpp @@ -63,6 +63,8 @@ void ShipInitialStatusDialog::on_velocitySpinBox_valueChanged(int value) } void ShipInitialStatusDialog::on_dockpointList_currentItemChanged(QListWidgetItem* current) { + if (!current) + return; cur_docker_point = current->data(Qt::UserRole).toInt(); updateUI(); } @@ -71,6 +73,11 @@ void ShipInitialStatusDialog::on_dockeeComboBox_currentIndexChanged(int index) auto dockeeData = ui->dockeeComboBox->itemData(index).toInt(); cur_dockee = dockeeData; _model->setDockee(cur_docker_point, dockeeData); + + if (cur_dockee >= 0 && ui->dockeePointComboBox->count() > 0) { + cur_dockee_point = ui->dockeePointComboBox->itemData(0).toInt(); + _model->setDockeePoint(cur_docker_point, cur_dockee_point); + } } void ShipInitialStatusDialog::on_dockeePointComboBox_currentIndexChanged(int index) { @@ -152,6 +159,10 @@ void ShipInitialStatusDialog::on_guardianSpinBox_valueChanged(int value) { _model->setGuardian(value); } +void ShipInitialStatusDialog::on_moveShipsCheckBox_toggled(bool value) +{ + _model->setMoveShipsWhenUndocking(value); +} void ShipInitialStatusDialog::updateUI() { util::SignalBlockers blockers(this); @@ -184,6 +195,7 @@ void ShipInitialStatusDialog::updateUI() updateFlags(); updateDocks(); updateDockee(); + ui->moveShipsCheckBox->setChecked(_model->getMoveShipsWhenUndocking()); updateSubsystems(); ui->colourComboBox->clear(); if (_model->getUseTeamcolours()) { @@ -218,7 +230,7 @@ void ShipInitialStatusDialog::updateFlags() } void ShipInitialStatusDialog::updateDocks() { - auto index = ui->dockpointList->currentIndex(); + int row = ui->dockpointList->currentRow(); ui->dockpointList->clear(); if (!_model->getIfMultpleShips()) { for (int dockpoint = 0; dockpoint < _model->getnum_dock_points(); dockpoint++) { @@ -229,7 +241,8 @@ void ShipInitialStatusDialog::updateDocks() ui->dockpointList->addItem(newItem); } } - ui->dockpointList->setCurrentIndex(index); + if (row >= 0 && row < ui->dockpointList->count()) + ui->dockpointList->setCurrentRow(row); if (cur_docker_point < 0) { // clear the dropdowns list_dockees(-1); diff --git a/qtfred/src/ui/dialogs/ShipEditor/ShipInitialStatusDialog.h b/qtfred/src/ui/dialogs/ShipEditor/ShipInitialStatusDialog.h index 03e8324cc22..434c6c3664f 100644 --- a/qtfred/src/ui/dialogs/ShipEditor/ShipInitialStatusDialog.h +++ b/qtfred/src/ui/dialogs/ShipEditor/ShipInitialStatusDialog.h @@ -47,6 +47,7 @@ class ShipInitialStatusDialog : public QDialog { void on_cargoEdit_editingFinished(); void on_cargoTitleEdit_editingFinished(); void on_colourComboBox_currentIndexChanged(int); + void on_moveShipsCheckBox_toggled(bool); private: // NOLINT(readability-redundant-access-specifiers) std::unique_ptr ui; diff --git a/qtfred/ui/ShipInitialStatus.ui b/qtfred/ui/ShipInitialStatus.ui index 79135521eb9..6bf149b0f5f 100644 --- a/qtfred/ui/ShipInitialStatus.ui +++ b/qtfred/ui/ShipInitialStatus.ui @@ -293,6 +293,16 @@ + + + + <html><head/><body><p>When unoccupying a dockpoint, move the smaller ship away so they don't overlap</p></body></html> + + + Move ships when undocking + + +