diff --git a/qtfred/help-src/doc/fundamentals.html b/qtfred/help-src/doc/fundamentals.html
index c6eb1410efa..b7fdb082e68 100644
--- a/qtfred/help-src/doc/fundamentals.html
+++ b/qtfred/help-src/doc/fundamentals.html
@@ -67,11 +67,11 @@
2. Place the ships
arrive immediately and that is fine for now.
3. Add a waypoint path for the transport
-Select the waypoint tool in the toolbar and Ctrl+click in the viewport
-to lay out a short route from the transport's position toward the Command Ship.
-Three or four points is plenty. Open the
-Waypoint Editor and name the path
-Transport Route.
+In the Other dropdown on the toolbar, select Waypoint,
+then Ctrl+Alt+click in the viewport to lay out a short route from the
+transport's position toward the Command Ship. Three or four points is plenty. Open
+the Waypoint Editor and name the
+path Transport Route.
Open the Ship Editor for Transport 1, go to
Initial Orders, and set its initial goal to
diff --git a/qtfred/help-src/doc/general/Toolbars.html b/qtfred/help-src/doc/general/Toolbars.html
index 4ffec618211..6c12a7b4186 100644
--- a/qtfred/help-src/doc/general/Toolbars.html
+++ b/qtfred/help-src/doc/general/Toolbars.html
@@ -134,15 +134,16 @@
Tools
Object creation dropdowns
-Two dropdowns at the right end of the toolbar control what is created when you
-click in the viewport.
+Three dropdowns at the right end of the toolbar control what is created when you
+click in the viewport. Each is paired with its own modifier chord, so you can place
+ships, props, and waypoints or jump nodes intermixed without switching modes.
| Dropdown | Used by | Description |
| Ships |
Ctrl+click |
- Selects the ship class, waypoint, or jump node to place. The object
- created on Ctrl+click is determined by this selection. |
+ Selects the ship class to place. The ship created on Ctrl+click is
+ determined by this selection. |
| Props |
@@ -150,6 +151,13 @@ Object creation dropdowns
Selects the prop class to place. The prop created on
Ctrl+Shift+click is determined by this selection. |
+
+ | Other |
+ Ctrl+Alt+click |
+ Selects what non-ship, non-prop object to place - Waypoint or
+ Jump Node. The object created on Ctrl+Alt+click is determined by this
+ selection. |
+
Context Bar
diff --git a/qtfred/help-src/doc/general/Viewport.html b/qtfred/help-src/doc/general/Viewport.html
index 28ce1eb5626..e0cf299a74b 100644
--- a/qtfred/help-src/doc/general/Viewport.html
+++ b/qtfred/help-src/doc/general/Viewport.html
@@ -12,11 +12,12 @@ Viewport
Creating objects
The toolbar dropdowns determine what type of object is created when you click in
-the viewport.
+the viewport. Each dropdown is paired with its own modifier chord:
- - Ctrl+click - creates a ship, waypoint, or jump node,
- depending on the toolbar selection.
- - Ctrl+Shift+click - creates a prop.
+ - Ctrl+click - creates a ship from the Ships dropdown.
+ - Ctrl+Shift+click - creates a prop from the Props dropdown.
+ - Ctrl+Alt+click - creates a waypoint or jump node from the
+ Other dropdown.
Selecting objects
diff --git a/qtfred/src/mission/Editor.cpp b/qtfred/src/mission/Editor.cpp
index 87aa811a3a2..32ee0ad5a15 100644
--- a/qtfred/src/mission/Editor.cpp
+++ b/qtfred/src/mission/Editor.cpp
@@ -656,8 +656,6 @@ void Editor::clearMission(bool fast_reload) {
}
void Editor::initialSetup() {
- Id_select_type_waypoint = static_cast(Ship_info.size());
- Id_select_type_jump_node = static_cast(Ship_info.size() + 1);
}
void Editor::setupCurrentObjectIndices(int selectedObj) {
diff --git a/qtfred/src/mission/Editor.h b/qtfred/src/mission/Editor.h
index 08636d1d70c..e4fca653382 100644
--- a/qtfred/src/mission/Editor.h
+++ b/qtfred/src/mission/Editor.h
@@ -189,9 +189,6 @@ class Editor : public QObject {
*/
bool autoload();
- int Id_select_type_jump_node = 0;
- int Id_select_type_waypoint = 0;
-
// object numbers for ships in a wing.
int wing_objects[MAX_WINGS][MAX_SHIPS_PER_WING];
diff --git a/qtfred/src/mission/EditorViewport.cpp b/qtfred/src/mission/EditorViewport.cpp
index 5cd10840271..28a916b27e8 100644
--- a/qtfred/src/mission/EditorViewport.cpp
+++ b/qtfred/src/mission/EditorViewport.cpp
@@ -1094,12 +1094,12 @@ void EditorViewport::drag_rotate_save_backup() {
}
int EditorViewport::create_object_on_grid(int x, int y, int waypoint_instance) {
- return create_object_on_grid(x, y, waypoint_instance, false);
+ return create_object_on_grid(x, y, waypoint_instance, CreateKind::Ship);
}
-int EditorViewport::create_object_on_grid(int x, int y, int waypoint_instance, bool create_prop) {
+int EditorViewport::create_object_on_grid(int x, int y, int waypoint_instance, CreateKind kind) {
float fallbackDist = 200.0f;
- if (create_prop) {
+ if (kind == CreateKind::Prop) {
if (cur_prop_index >= 0 && cur_prop_index < prop_info_size()) {
prop_info* pip = &Prop_info[cur_prop_index];
if (pip->model_num >= 0) {
@@ -1112,16 +1112,14 @@ int EditorViewport::create_object_on_grid(int x, int y, int waypoint_instance, b
}
}
}
- } else if (cur_model_index >= 0 && cur_model_index < (int)Ship_info.size() &&
- cur_model_index != editor->Id_select_type_waypoint &&
- cur_model_index != editor->Id_select_type_jump_node &&
+ } else if (kind == CreateKind::Ship && cur_model_index >= 0 && cur_model_index < (int)Ship_info.size() &&
Ship_info[cur_model_index].model_num >= 0) {
fallbackDist = model_get_radius(Ship_info[cur_model_index].model_num) * 1.5f;
}
vec3d pos = getCreatePosition(x, y, fallbackDist);
editor->unmark_all();
- int obj = create_object(&pos, waypoint_instance, create_prop);
+ int obj = create_object(&pos, waypoint_instance, kind);
if (obj >= 0) {
editor->markObject(obj);
@@ -1134,10 +1132,10 @@ int EditorViewport::create_object_on_grid(int x, int y, int waypoint_instance, b
return obj;
}
-int EditorViewport::create_object(vec3d* pos, int waypoint_instance, bool create_prop) {
+int EditorViewport::create_object(vec3d* pos, int waypoint_instance, CreateKind kind) {
int obj, n;
- if (create_prop) {
+ if (kind == CreateKind::Prop) {
if (cur_prop_index < 0 || cur_prop_index >= prop_info_size()) {
return -1;
}
@@ -1146,17 +1144,26 @@ int EditorViewport::create_object(vec3d* pos, int waypoint_instance, bool create
if (obj == -1) {
return -1;
}
- } else {
-
- if (cur_model_index == editor->Id_select_type_waypoint) {
+ } else if (kind == CreateKind::Other) {
+ switch (cur_other_kind) {
+ case OtherKind::Waypoint:
obj = editor->create_waypoint(pos, waypoint_instance);
- } else if (cur_model_index == editor->Id_select_type_jump_node) {
+ break;
+ case OtherKind::JumpNode: {
CJumpNode jnp(pos);
obj = jnp.GetSCPObjectNumber();
Jump_nodes.push_back(std::move(jnp));
- } else if(Ship_info[cur_model_index].flags[Ship::Info_Flags::No_fred]){
+ break;
+ }
+ default:
obj = -1;
- } else { // creating a ship
+ break;
+ }
+ } else { // CreateKind::Ship
+ if (cur_model_index < 0 || cur_model_index >= (int)Ship_info.size() ||
+ Ship_info[cur_model_index].flags[Ship::Info_Flags::No_fred]) {
+ obj = -1;
+ } else {
obj = editor->create_ship(nullptr, pos, cur_model_index);
if (obj == -1)
return -1;
@@ -1193,7 +1200,7 @@ int EditorViewport::createShipAtScreenPos(int x, int y, int modelIndex) {
}
int savedModelIndex = cur_model_index;
cur_model_index = modelIndex;
- int obj = create_object_on_grid(x, y, -1, false);
+ int obj = create_object_on_grid(x, y, -1, CreateKind::Ship);
cur_model_index = savedModelIndex;
return obj;
}
@@ -1205,29 +1212,30 @@ int EditorViewport::createPropAtScreenPos(int x, int y, int propIndex) {
}
int savedPropIndex = cur_prop_index;
cur_prop_index = propIndex;
- int obj = create_object_on_grid(x, y, -1, true);
+ int obj = create_object_on_grid(x, y, -1, CreateKind::Prop);
cur_prop_index = savedPropIndex;
return obj;
}
int EditorViewport::createWaypointAtScreenPos(int x, int y, int waypoint_instance) {
- int savedModelIndex = cur_model_index;
- cur_model_index = editor->Id_select_type_waypoint;
- int obj = create_object_on_grid(x, y, waypoint_instance, false);
- cur_model_index = savedModelIndex;
+ OtherKind savedKind = cur_other_kind;
+ cur_other_kind = OtherKind::Waypoint;
+ int obj = create_object_on_grid(x, y, waypoint_instance, CreateKind::Other);
+ cur_other_kind = savedKind;
return obj;
}
int EditorViewport::createJumpNodeAtScreenPos(int x, int y) {
- int savedModelIndex = cur_model_index;
- cur_model_index = editor->Id_select_type_jump_node;
- int obj = create_object_on_grid(x, y, -1, false);
- cur_model_index = savedModelIndex;
+ OtherKind savedKind = cur_other_kind;
+ cur_other_kind = OtherKind::JumpNode;
+ int obj = create_object_on_grid(x, y, -1, CreateKind::Other);
+ cur_other_kind = savedKind;
return obj;
}
void EditorViewport::initialSetup() {
cur_model_index = get_default_player_ship_index();
+ cur_other_kind = OtherKind::Waypoint;
for (int i = 0; i < prop_info_size(); ++i) {
if (!Prop_info[i].flags[Prop::Info_Flags::No_fred]) {
cur_prop_index = i;
diff --git a/qtfred/src/mission/EditorViewport.h b/qtfred/src/mission/EditorViewport.h
index bd12eae0624..ec4f4cd6850 100644
--- a/qtfred/src/mission/EditorViewport.h
+++ b/qtfred/src/mission/EditorViewport.h
@@ -17,6 +17,17 @@ struct Marking_box {
int y2 = 0;
};
+enum class CreateKind {
+ Ship,
+ Prop,
+ Other,
+};
+
+enum class OtherKind {
+ Waypoint,
+ JumpNode,
+};
+
struct ViewSettings {
bool Universal_heading = false;
bool Show_stars = true;
@@ -129,9 +140,9 @@ class EditorViewport {
void drag_rotate_save_backup();
int create_object_on_grid(int x, int y, int waypoint_instance);
- int create_object_on_grid(int x, int y, int waypoint_instance, bool create_prop);
+ int create_object_on_grid(int x, int y, int waypoint_instance, CreateKind kind);
- int create_object(vec3d *pos, int waypoint_instance = -1, bool create_prop = false);
+ int create_object(vec3d *pos, int waypoint_instance = -1, CreateKind kind = CreateKind::Ship);
vec3d getCreatePosition(int x, int y, float fallbackDist);
int createShipAtScreenPos(int x, int y, int modelIndex);
@@ -170,6 +181,7 @@ class EditorViewport {
int cur_model_index = 0;
int cur_prop_index = -1;
+ OtherKind cur_other_kind = OtherKind::Waypoint;
object_orient_pos rotation_backup[MAX_OBJECTS];
diff --git a/qtfred/src/ui/FredView.cpp b/qtfred/src/ui/FredView.cpp
index b4b190355e9..4aae189d081 100644
--- a/qtfred/src/ui/FredView.cpp
+++ b/qtfred/src/ui/FredView.cpp
@@ -187,7 +187,8 @@ void FredView::setEditor(Editor* editor, EditorViewport* viewport) {
ui->toolBar->addWidget(shipsLabel);
_shipClassBox = new ObjectComboBox(ui->toolBar);
_shipClassBox->setFixedWidth(150);
- _shipClassBox->initForShips(_viewport);
+ _shipClassBox->setToolTip(tr("Ctrl+click in the viewport to place"));
+ _shipClassBox->initForShips();
ui->toolBar->addWidget(_shipClassBox);
connect(_shipClassBox, &ObjectComboBox::classSelected, this, &FredView::onShipClassSelected);
@@ -196,10 +197,21 @@ void FredView::setEditor(Editor* editor, EditorViewport* viewport) {
ui->toolBar->addWidget(propsLabel);
_propClassBox = new ObjectComboBox(ui->toolBar);
_propClassBox->setFixedWidth(150);
+ _propClassBox->setToolTip(tr("Ctrl+Shift+click in the viewport to place"));
_propClassBox->initForProps();
ui->toolBar->addWidget(_propClassBox);
connect(_propClassBox, &ObjectComboBox::classSelected, this, &FredView::onPropClassSelected);
+ auto otherLabel = new QLabel(tr("Other: "), ui->toolBar);
+ otherLabel->setContentsMargins(4, 0, 0, 0);
+ ui->toolBar->addWidget(otherLabel);
+ _otherClassBox = new ObjectComboBox(ui->toolBar);
+ _otherClassBox->setFixedWidth(150);
+ _otherClassBox->setToolTip(tr("Ctrl+Alt+click in the viewport to place"));
+ _otherClassBox->initForOther();
+ ui->toolBar->addWidget(_otherClassBox);
+ connect(_otherClassBox, &ObjectComboBox::classSelected, this, &FredView::onOtherKindSelected);
+
initializeContextToolbar();
initializeTransformBar();
@@ -228,6 +240,7 @@ void FredView::setEditor(Editor* editor, EditorViewport* viewport) {
connect(this, &FredView::viewIdle, this, &FredView::onUpdateSelectionLock);
connect(this, &FredView::viewIdle, this, &FredView::onUpdateShipClassBox);
connect(this, &FredView::viewIdle, this, &FredView::onUpdatePropClassBox);
+ connect(this, &FredView::viewIdle, this, &FredView::onUpdateOtherClassBox);
connect(this, &FredView::viewIdle, this, &FredView::onUpdateEditorActions);
connect(this, &FredView::viewIdle, this, &FredView::onUpdateWingActionStatus);
connect(this, &FredView::viewIdle, this, &FredView::onUpdateContextToolbar);
@@ -1876,7 +1889,9 @@ void FredView::initializePopupMenus() {
});
_createSubmenu->addMenu(_createPropSubmenu);
- auto* createWaypointAction = new QAction(tr("Waypoint"), _createSubmenu);
+ auto* createOtherSubmenu = new QMenu(tr("Other"), _createSubmenu);
+
+ auto* createWaypointAction = new QAction(tr("Waypoint"), createOtherSubmenu);
connect(createWaypointAction, &QAction::triggered, this, [this]() {
int waypoint_instance = -1;
if (fred->cur_waypoint != nullptr) {
@@ -1884,13 +1899,15 @@ void FredView::initializePopupMenus() {
}
_viewport->createWaypointAtScreenPos(_lastContextMenuLocalPos.x(), _lastContextMenuLocalPos.y(), waypoint_instance);
});
- _createSubmenu->addAction(createWaypointAction);
+ createOtherSubmenu->addAction(createWaypointAction);
- auto* createJumpNodeAction = new QAction(tr("Jump Node"), _createSubmenu);
+ auto* createJumpNodeAction = new QAction(tr("Jump Node"), createOtherSubmenu);
connect(createJumpNodeAction, &QAction::triggered, this, [this]() {
_viewport->createJumpNodeAtScreenPos(_lastContextMenuLocalPos.x(), _lastContextMenuLocalPos.y());
});
- _createSubmenu->addAction(createJumpNodeAction);
+ createOtherSubmenu->addAction(createJumpNodeAction);
+
+ _createSubmenu->addMenu(createOtherSubmenu);
_viewPopup->addMenu(_createSubmenu);
_viewPopup->addSeparator();
@@ -2349,12 +2366,18 @@ void FredView::onUpdatePropClassBox() {
}
_propClassBox->selectClass(_viewport->cur_prop_index);
}
+void FredView::onUpdateOtherClassBox() {
+ _otherClassBox->selectClass(static_cast(_viewport->cur_other_kind));
+}
void FredView::onShipClassSelected(int ship_class) {
_viewport->cur_model_index = ship_class;
}
void FredView::onPropClassSelected(int prop_class) {
_viewport->cur_prop_index = prop_class;
}
+void FredView::onOtherKindSelected(int other_kind) {
+ _viewport->cur_other_kind = static_cast(other_kind);
+}
void FredView::on_actionAsteroid_Field_triggered(bool) {
auto asteroidFieldEditor = new dialogs::AsteroidEditorDialog(this, _viewport);
asteroidFieldEditor->setAttribute(Qt::WA_DeleteOnClose);
diff --git a/qtfred/src/ui/FredView.h b/qtfred/src/ui/FredView.h
index d6b3697c5da..f313294d068 100644
--- a/qtfred/src/ui/FredView.h
+++ b/qtfred/src/ui/FredView.h
@@ -277,6 +277,7 @@ class FredView: public QMainWindow, public IDialogProvider {
ObjectComboBox* _shipClassBox = nullptr;
ObjectComboBox* _propClassBox = nullptr;
+ ObjectComboBox* _otherClassBox = nullptr;
Editor* fred = nullptr;
EditorViewport* _viewport = nullptr;
@@ -297,6 +298,7 @@ class FredView: public QMainWindow, public IDialogProvider {
void onUpdateSelectionLock();
void onUpdateShipClassBox();
void onUpdatePropClassBox();
+ void onUpdateOtherClassBox();
void onUpdateEditorActions();
void onUpdateWingActionStatus();
@@ -341,6 +343,7 @@ class FredView: public QMainWindow, public IDialogProvider {
void onShipClassSelected(int ship_class);
void onPropClassSelected(int prop_class);
+ void onOtherKindSelected(int other_kind);
void windowActivated();
void windowDeactivated();
diff --git a/qtfred/src/ui/widgets/ObjectComboBox.cpp b/qtfred/src/ui/widgets/ObjectComboBox.cpp
index b7f21b607cf..7c076978efa 100644
--- a/qtfred/src/ui/widgets/ObjectComboBox.cpp
+++ b/qtfred/src/ui/widgets/ObjectComboBox.cpp
@@ -22,8 +22,7 @@ ObjectComboBox::ObjectComboBox(QWidget* parent) : QComboBox(parent) {
&ObjectComboBox::indexChanged);
}
-void ObjectComboBox::initForShips(EditorViewport* viewport) {
- _viewport = viewport;
+void ObjectComboBox::initForShips() {
fredApp->runAfterInit([this]() {
buildShipsModel();
});
@@ -35,6 +34,12 @@ void ObjectComboBox::initForProps() {
});
}
+void ObjectComboBox::initForOther() {
+ fredApp->runAfterInit([this]() {
+ buildOtherModel();
+ });
+}
+
void ObjectComboBox::buildShipsModel() {
auto model = new QStandardItemModel();
@@ -50,16 +55,18 @@ void ObjectComboBox::buildShipsModel() {
model->appendRow(item);
}
- auto separator = new QStandardItem();
- separator->setData("separator", Qt::AccessibleDescriptionRole);
- model->appendRow(separator);
+ setModel(model);
+}
+
+void ObjectComboBox::buildOtherModel() {
+ auto model = new QStandardItemModel();
auto waypoint = new QStandardItem("Waypoint");
- waypoint->setData(_viewport->editor->Id_select_type_waypoint, Qt::UserRole);
+ waypoint->setData(static_cast(OtherKind::Waypoint), Qt::UserRole);
model->appendRow(waypoint);
auto jumpNode = new QStandardItem("Jump Node");
- jumpNode->setData(_viewport->editor->Id_select_type_jump_node, Qt::UserRole);
+ jumpNode->setData(static_cast(OtherKind::JumpNode), Qt::UserRole);
model->appendRow(jumpNode);
setModel(model);
diff --git a/qtfred/src/ui/widgets/ObjectComboBox.h b/qtfred/src/ui/widgets/ObjectComboBox.h
index f1f67a8a5d5..21e286ab40c 100644
--- a/qtfred/src/ui/widgets/ObjectComboBox.h
+++ b/qtfred/src/ui/widgets/ObjectComboBox.h
@@ -10,13 +10,12 @@ namespace fso::fred {
class ObjectComboBox : public QComboBox {
Q_OBJECT
- EditorViewport* _viewport = nullptr;
-
public:
explicit ObjectComboBox(QWidget* parent = nullptr);
- void initForShips(EditorViewport* viewport);
+ void initForShips();
void initForProps();
+ void initForOther();
void selectClass(int class_index);
@@ -26,6 +25,7 @@ class ObjectComboBox : public QComboBox {
private:
void buildShipsModel();
void buildPropsModel();
+ void buildOtherModel();
void indexChanged(int index);
};
diff --git a/qtfred/src/ui/widgets/renderwidget.cpp b/qtfred/src/ui/widgets/renderwidget.cpp
index 481993bb91f..7cc6cc8752e 100644
--- a/qtfred/src/ui/widgets/renderwidget.cpp
+++ b/qtfred/src/ui/widgets/renderwidget.cpp
@@ -229,8 +229,15 @@ void RenderWidget::mousePressEvent(QMouseEvent* event) {
if (event->modifiers().testFlag(Qt::ControlModifier)) { // add a new object
if (_viewport->on_object == -1) {
_viewport->Selection_lock = false; // force off selection lock
- auto spawn_prop = event->modifiers().testFlag(Qt::ShiftModifier);
- _viewport->on_object = _viewport->create_object_on_grid(event->x(), event->y(), waypoint_instance, spawn_prop);
+ const bool shift = event->modifiers().testFlag(Qt::ShiftModifier);
+ const bool alt = event->modifiers().testFlag(Qt::AltModifier);
+ auto kind = CreateKind::Ship;
+ if (alt && !shift) {
+ kind = CreateKind::Other;
+ } else if (shift && !alt) {
+ kind = CreateKind::Prop;
+ }
+ _viewport->on_object = _viewport->create_object_on_grid(event->x(), event->y(), waypoint_instance, kind);
} else {
_viewport->Dup_drag = 1;