Compare commits
5 commits
186d64f28e
...
f81964e395
Author | SHA1 | Date | |
---|---|---|---|
f81964e395 | |||
bfaab69daa | |||
75be686e48 | |||
45f39c3f49 | |||
12a4ed76b4 |
10 changed files with 110 additions and 16 deletions
|
@ -14,6 +14,8 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||||
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets LinguistTools)
|
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets LinguistTools)
|
||||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets LinguistTools)
|
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets LinguistTools)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
set(TS_FILES editor_en_US.ts)
|
set(TS_FILES editor_en_US.ts)
|
||||||
|
|
||||||
set(PROJECT_SOURCES
|
set(PROJECT_SOURCES
|
||||||
|
@ -65,6 +67,16 @@ endif()
|
||||||
target_include_directories(editor PUBLIC "../core/src" "../include")
|
target_include_directories(editor PUBLIC "../core/src" "../include")
|
||||||
target_link_libraries(editor PRIVATE openblocks Qt${QT_VERSION_MAJOR}::Widgets)
|
target_link_libraries(editor PRIVATE openblocks Qt${QT_VERSION_MAJOR}::Widgets)
|
||||||
|
|
||||||
|
# Qt6 does not include QOpenGLWidgets as part of Widgets base anymore, so
|
||||||
|
# we have to include it manually
|
||||||
|
if (${QT_VERSION} GREATER_EQUAL 6)
|
||||||
|
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS OpenGL OpenGLWidgets)
|
||||||
|
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS OpenGL OpenGLWidgets)
|
||||||
|
|
||||||
|
target_include_directories(editor PUBLIC Qt6::OpenGL Qt6::OpenGLWidgets)
|
||||||
|
target_link_libraries(editor PRIVATE Qt6::OpenGL Qt6::OpenGLWidgets)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
|
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
|
||||||
# If you are developing for iOS or macOS you should consider setting an
|
# If you are developing for iOS or macOS you should consider setting an
|
||||||
# explicit, fixed bundle identifier manually though.
|
# explicit, fixed bundle identifier manually though.
|
||||||
|
|
|
@ -138,7 +138,9 @@ void MainGLWidget::handleHandleDrag(QMouseEvent* evt) {
|
||||||
|
|
||||||
// Apply snapping in the current frame
|
// Apply snapping in the current frame
|
||||||
glm::vec3 diff = centerPoint - (glm::vec3)editorToolHandles->adornee->lock()->position();
|
glm::vec3 diff = centerPoint - (glm::vec3)editorToolHandles->adornee->lock()->position();
|
||||||
if (snappingFactor()) diff = frame * (glm::round(glm::vec3(frame.Inverse() * diff) / snappingFactor()) * snappingFactor());
|
// printf("\n=======\nPre-snap: (%f, %f, %f)\n", diff.x, diff.y, diff.z);
|
||||||
|
if (snappingFactor()) diff = frame.Rotation() * (glm::round(glm::vec3(frame.Inverse().Rotation() * diff) / snappingFactor()) * snappingFactor());
|
||||||
|
// printf("Post-snap: (%f, %f, %f)\n", diff.x, diff.y, diff.z);
|
||||||
|
|
||||||
switch (mainWindow()->selectedTool) {
|
switch (mainWindow()->selectedTool) {
|
||||||
case SelectedTool::SELECT: break;
|
case SelectedTool::SELECT: break;
|
||||||
|
|
|
@ -72,20 +72,18 @@ MainWindow::MainWindow(QWidget *parent)
|
||||||
connect(ui->actionSave, &QAction::triggered, this, [&]() {
|
connect(ui->actionSave, &QAction::triggered, this, [&]() {
|
||||||
std::optional<std::string> path;
|
std::optional<std::string> path;
|
||||||
if (!dataModel->HasFile())
|
if (!dataModel->HasFile())
|
||||||
path = QFileDialog::getSaveFileName(this, QString::fromStdString("Save " + dataModel->name), "", "*.obl").toStdString();
|
path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptSave, QString::fromStdString("Save " + dataModel->name));
|
||||||
if (path == "") return;
|
if (path == "") return;
|
||||||
|
|
||||||
dataModel->SaveToFile(path);
|
dataModel->SaveToFile(path);
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(ui->actionOpen, &QAction::triggered, this, [&]() {
|
connect(ui->actionOpen, &QAction::triggered, this, [&]() {
|
||||||
std::string path = QFileDialog::getOpenFileName(this, "Load file", "", "*.obl").toStdString();
|
std::optional<std::string> path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptOpen);
|
||||||
if (path == "") return;
|
if (!path) return;
|
||||||
std::shared_ptr<DataModel> newModel = DataModel::LoadFromFile(path);
|
std::shared_ptr<DataModel> newModel = DataModel::LoadFromFile(path.value());
|
||||||
dataModel = newModel;
|
dataModel = newModel;
|
||||||
delete ui->explorerView->selectionModel();
|
ui->explorerView->updateRoot(newModel);
|
||||||
ui->explorerView->reset();
|
|
||||||
ui->explorerView->setModel(new ExplorerModel(dataModel));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(ui->actionDelete, &QAction::triggered, this, [&]() {
|
connect(ui->actionDelete, &QAction::triggered, this, [&]() {
|
||||||
|
@ -159,6 +157,40 @@ MainWindow::MainWindow(QWidget *parent)
|
||||||
selectedParent->AddChild(inst);
|
selectedParent->AddChild(inst);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(ui->actionSaveModel, &QAction::triggered, this, [&]() {
|
||||||
|
std::optional<std::string> path = openFileDialog("Openblocks Model (*.obm)", ".obm", QFileDialog::AcceptSave);
|
||||||
|
if (!path) return;
|
||||||
|
std::ofstream outStream(path.value());
|
||||||
|
|
||||||
|
// Serialized XML for exporting
|
||||||
|
pugi::xml_document modelDoc;
|
||||||
|
pugi::xml_node modelRoot = modelDoc.append_child("openblocks");
|
||||||
|
|
||||||
|
for (InstanceRefWeak inst : getSelection()) {
|
||||||
|
if (inst.expired()) continue;
|
||||||
|
inst.lock()->Serialize(&modelRoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
modelDoc.save(outStream);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(ui->actionInsertModel, &QAction::triggered, this, [&]() {
|
||||||
|
if (getSelection().size() != 1 || getSelection()[0].expired()) return;
|
||||||
|
InstanceRef selectedParent = getSelection()[0].lock();
|
||||||
|
|
||||||
|
std::optional<std::string> path = openFileDialog("Openblocks Model (*.obm)", ".obm", QFileDialog::AcceptOpen);
|
||||||
|
if (!path) return;
|
||||||
|
std::ifstream inStream(path.value());
|
||||||
|
|
||||||
|
pugi::xml_document modelDoc;
|
||||||
|
modelDoc.load(inStream);
|
||||||
|
|
||||||
|
for (pugi::xml_node instNode : modelDoc.child("openblocks").children("Item")) {
|
||||||
|
InstanceRef inst = Instance::Deserialize(&instNode);
|
||||||
|
selectedParent->AddChild(inst);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Update handles
|
// Update handles
|
||||||
addSelectionListener([&](auto oldSelection, auto newSelection, bool fromExplorer) {
|
addSelectionListener([&](auto oldSelection, auto newSelection, bool fromExplorer) {
|
||||||
|
@ -239,6 +271,18 @@ void MainWindow::updateToolbars() {
|
||||||
: HandlesType::ScaleHandles;
|
: HandlesType::ScaleHandles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> MainWindow::openFileDialog(QString filter, QString defaultExtension, QFileDialog::AcceptMode acceptMode, QString title) {
|
||||||
|
QFileDialog dialog(this);
|
||||||
|
if (title != "") dialog.setWindowTitle(title);
|
||||||
|
dialog.setNameFilters(QStringList { filter, "All Files (*)" });
|
||||||
|
dialog.setDefaultSuffix(defaultExtension);
|
||||||
|
dialog.setAcceptMode(acceptMode);
|
||||||
|
if (!dialog.exec())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return dialog.selectedFiles().front().toStdString();
|
||||||
|
}
|
||||||
|
|
||||||
MainWindow::~MainWindow()
|
MainWindow::~MainWindow()
|
||||||
{
|
{
|
||||||
delete ui;
|
delete ui;
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "qmenu.h"
|
#include "qmenu.h"
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
|
#include <qfiledialog.h>
|
||||||
|
|
||||||
enum SelectedTool {
|
enum SelectedTool {
|
||||||
SELECT,
|
SELECT,
|
||||||
|
@ -44,5 +45,7 @@ private:
|
||||||
|
|
||||||
void updateToolbars();
|
void updateToolbars();
|
||||||
void timerEvent(QTimerEvent*) override;
|
void timerEvent(QTimerEvent*) override;
|
||||||
|
|
||||||
|
std::optional<std::string> openFileDialog(QString filter, QString defaultExtension, QFileDialog::AcceptMode acceptMode, QString title = "");
|
||||||
};
|
};
|
||||||
#endif // MAINWINDOW_H
|
#endif // MAINWINDOW_H
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>1027</width>
|
<width>1027</width>
|
||||||
<height>22</height>
|
<height>30</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QMenu" name="menuFile">
|
<widget class="QMenu" name="menuFile">
|
||||||
|
@ -406,6 +406,28 @@
|
||||||
<enum>QAction::MenuRole::NoRole</enum>
|
<enum>QAction::MenuRole::NoRole</enum>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionSaveModel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Save Model to File</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Saves objects to file as XML model</string>
|
||||||
|
</property>
|
||||||
|
<property name="menuRole">
|
||||||
|
<enum>QAction::MenuRole::NoRole</enum>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionInsertModel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Insert Model from File</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Insert model from XML file</string>
|
||||||
|
</property>
|
||||||
|
<property name="menuRole">
|
||||||
|
<enum>QAction::MenuRole::NoRole</enum>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
|
|
|
@ -234,6 +234,12 @@ bool ExplorerModel::dropMimeData(const QMimeData *data, Qt::DropAction action, i
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExplorerModel::updateRoot(InstanceRef newRoot) {
|
||||||
|
beginResetModel();
|
||||||
|
rootItem = newRoot;
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
QMimeData* ExplorerModel::mimeData(const QModelIndexList& indexes) const {
|
QMimeData* ExplorerModel::mimeData(const QModelIndexList& indexes) const {
|
||||||
// application/x-openblocks-instance-pointers
|
// application/x-openblocks-instance-pointers
|
||||||
DragDropSlot* slot = new DragDropSlot();
|
DragDropSlot* slot = new DragDropSlot();
|
||||||
|
|
|
@ -1,18 +1,12 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "objects/base/instance.h"
|
#include "objects/base/instance.h"
|
||||||
#include "objects/part.h"
|
|
||||||
#include "qabstractitemmodel.h"
|
#include "qabstractitemmodel.h"
|
||||||
#include "qevent.h"
|
#include "qevent.h"
|
||||||
#include "qmenu.h"
|
|
||||||
#include "qnamespace.h"
|
#include "qnamespace.h"
|
||||||
#include "qtreeview.h"
|
|
||||||
#include <QOpenGLWidget>
|
#include <QOpenGLWidget>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
// #ifndef EXPLORERMODEL_H
|
|
||||||
// #define EXPLORERMODEL_H
|
|
||||||
|
|
||||||
class ExplorerModel : public QAbstractItemModel {
|
class ExplorerModel : public QAbstractItemModel {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -42,6 +36,8 @@ public:
|
||||||
Qt::DropActions supportedDropActions() const override;
|
Qt::DropActions supportedDropActions() const override;
|
||||||
InstanceRef fromIndex(const QModelIndex index) const;
|
InstanceRef fromIndex(const QModelIndex index) const;
|
||||||
QModelIndex ObjectToIndex(InstanceRef item);
|
QModelIndex ObjectToIndex(InstanceRef item);
|
||||||
|
|
||||||
|
void updateRoot(InstanceRef newRoot);
|
||||||
private:
|
private:
|
||||||
InstanceRef rootItem;
|
InstanceRef rootItem;
|
||||||
QModelIndex toIndex(InstanceRef item);
|
QModelIndex toIndex(InstanceRef item);
|
||||||
|
|
|
@ -73,7 +73,6 @@ void ExplorerView::keyPressEvent(QKeyEvent* event) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ExplorerView::buildContextMenu() {
|
void ExplorerView::buildContextMenu() {
|
||||||
contextMenu.addAction(M_mainWindow->ui->actionDelete);
|
contextMenu.addAction(M_mainWindow->ui->actionDelete);
|
||||||
contextMenu.addSeparator();
|
contextMenu.addSeparator();
|
||||||
|
@ -81,4 +80,11 @@ void ExplorerView::buildContextMenu() {
|
||||||
contextMenu.addAction(M_mainWindow->ui->actionCut);
|
contextMenu.addAction(M_mainWindow->ui->actionCut);
|
||||||
contextMenu.addAction(M_mainWindow->ui->actionPaste);
|
contextMenu.addAction(M_mainWindow->ui->actionPaste);
|
||||||
contextMenu.addAction(M_mainWindow->ui->actionPasteInto);
|
contextMenu.addAction(M_mainWindow->ui->actionPasteInto);
|
||||||
|
contextMenu.addSeparator();
|
||||||
|
contextMenu.addAction(M_mainWindow->ui->actionSaveModel);
|
||||||
|
contextMenu.addAction(M_mainWindow->ui->actionInsertModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExplorerView::updateRoot(InstanceRef newRoot) {
|
||||||
|
model.updateRoot(newRoot);
|
||||||
}
|
}
|
|
@ -22,6 +22,7 @@ public:
|
||||||
// void dropEvent(QDropEvent*) override;
|
// void dropEvent(QDropEvent*) override;
|
||||||
|
|
||||||
void buildContextMenu();
|
void buildContextMenu();
|
||||||
|
void updateRoot(InstanceRef newRoot);
|
||||||
private:
|
private:
|
||||||
ExplorerModel model;
|
ExplorerModel model;
|
||||||
QMenu contextMenu;
|
QMenu contextMenu;
|
||||||
|
|
2
run.sh
2
run.sh
|
@ -1,3 +1,5 @@
|
||||||
|
if [ $# -eq 0 ] || ([ "$1" != "editor" ] && [ "$1" != "client" ]); then echo "Argument missing, must be 'client' or 'editor'"; exit; fi
|
||||||
|
|
||||||
[ "$2" = "-debug" ] && CMAKE_OPTS=-DCMAKE_BUILD_TYPE=Debug
|
[ "$2" = "-debug" ] && CMAKE_OPTS=-DCMAKE_BUILD_TYPE=Debug
|
||||||
[ "$2" = "-release" ] && CMAKE_OPTS=-DCMAKE_BUILD_TYPE=Release
|
[ "$2" = "-release" ] && CMAKE_OPTS=-DCMAKE_BUILD_TYPE=Release
|
||||||
[ "$2" = "-reldbg" ] && CMAKE_OPTS=-DCMAKE_BUILD_TYPE=RelWithDebInfo
|
[ "$2" = "-reldbg" ] && CMAKE_OPTS=-DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||||
|
|
Loading…
Add table
Reference in a new issue