Open/load project

This commit is contained in:
Anthony Rabine 2023-05-11 12:16:59 +02:00
parent 85bf8d8e32
commit fc46659e42
19 changed files with 361 additions and 225 deletions

96
story-editor/example.asm Normal file
View file

@ -0,0 +1,96 @@
; jump over the data, to our entry label
jump .entry
; Constant elements are separated by commas
$imageBird DC8, "example.bmp", 8 ; string of chars, followed by one byte
$soundChoice DC8, "choose1.snd"
$yaya DC8, "yaya.bmp"
$rabbit DC8, "rabbit.bmp"
$someConstant DC32, 12456789
; Liste des noeuds à appeler
$ChoiceObject DC32, 2, .MEDIA_02, .MEDIA_03
; DVsxx to declare a variable in RAM, followed by the number of elements
$MyArray DV8, 10 ; array of 10 bytes
$RamData1 DV32, 1 ; one 32-bit integer
$ChoiceMem DV32, 10 ; 10 elements for the choices, to be generated
; label definition
.entry: ;; comment here should work
; Syscall test: show image and play sound
lcons r0, $imageBird ; image name address in ROM located in R0 (null terminated)
lcons r1, $soundChoice ; set to 0 if no sound
syscall 1
lcons r0, $ChoiceObject
jump .media ; no return possible, so a jump is enough
; Generic media choice manager
.media:
; Les adresses des différents medias sont dans la stack
; Arguments:
; r0: address d'une structure de type "media choice"
; Local:
; t0: loop counter
; t1: increment 1
; t2: increment 4
; t3: current media address
.media_loop_start:
load t0, @r0, 4 ; Le premier élément est le nombre de choix possibles, t0 = 3 (exemple)
lcons t1, 1
lcons t2, 4
mov t3, r0
.media_loop:
add t3, t2 ; @++
; ------- On appelle un autre media node
push r0 ; save r0
load r0, @t3, 4 ; r0 = content in ram at address in T4
call r0
pop r0
; TODO: wait for event
sub t0, t1 ; i--
skipnz t0 ; if (r0) goto start_loop;
jump .media_loop_start
jump .media_loop
.MEDIA_02:
lcons r0, $yaya ; image name address in ROM located in R0 (null terminated)
lcons r1, $soundChoice ; set to 0 if no sound
syscall 1
ret
.MEDIA_03:
lcons r0, $rabbit
lcons r1, $soundChoice
syscall 1
ret
.SYSCALL_TEST:
; syscall test: wait for event
lcons r0, 0xFF ; wait for all event, blocking
syscall 2
; We create a stupid loop just for RAM variable testing
lcons r0, 4 ; prepare loop: 4 iterations
lcons r6, $RamData1 ; store address to R6
store @r6, r0, 4 ; save R0 in RAM
lcons r1, 1
.loop:
load r0, @r6, 4 ; load this variable
sub r0, r1
store @r6, r0, 4 ; save R0 in RAM
skipz r0 ; skip loop if R0 == 0
jump .loop
mov r0, r2 ; copy R2 into R0 (blank space between , and R2)
mov R0,R2 ; copy R2 into R0 (NO blank space between , and R2)
halt

View file

@ -17,9 +17,30 @@ bool EventFilter::eventFilter( QObject *obj, QEvent *event )
} }
DockWidgetBase::DockWidgetBase(const QString &title) DockWidgetBase::DockWidgetBase(const QString &title, bool visibility)
: QDockWidget(title) : QDockWidget(title)
, m_visibility(visibility ? tribool::True : tribool::False)
{ {
EventFilter* filter = new EventFilter( this ); EventFilter* filter = new EventFilter( this );
installEventFilter( filter ); installEventFilter( filter );
setAllowedAreas(Qt::AllDockWidgetAreas);
} }
void DockWidgetBase::Close()
{
// Memorize prefered visibility
// m_visibility = isVisible() ? tribool::True : tribool::False;
hide();
}
void DockWidgetBase::Open()
{
// Restore prefered visibility
if (m_visibility) {
show();
} else {
hide();
}
}

View file

@ -17,7 +17,14 @@ class DockWidgetBase : public QDockWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
DockWidgetBase(const QString &title); DockWidgetBase(const QString &title, bool visibility);
void Close();
void Open();
private:
enum tribool: uint8_t {False = 0, True = 1, Unknown = 2};
tribool m_visibility{Unknown};
}; };
#endif // DOCK_WIDGET_BASE_H #endif // DOCK_WIDGET_BASE_H

View file

@ -1,9 +1,9 @@
#include "log_dock.h" #include "log_dock.h"
LogDock::LogDock() LogDock::LogDock()
: DockWidgetBase(tr("Logs")) : DockWidgetBase(tr("Logs"), true)
{ {
setObjectName("OstHmiDock"); // used to save the state setObjectName("LogsDock"); // used to save the state
m_logUi.setupUi(this); m_logUi.setupUi(this);
} }

View file

@ -51,12 +51,12 @@ typedef void (*message_output_t)(QtMsgType , const QMessageLogContext &, const Q
MainWindow::MainWindow() MainWindow::MainWindow()
: m_model(m_project) : m_model(m_project)
, m_scene(m_model) , m_scene(m_model)
, m_settings("D8S", "OpenStoryTeller") , m_settings("OpenStoryTeller", "OpenStoryTellerEditor")
{ {
m_project.Clear(); m_project.Clear();
// SetupTemporaryProject();
// RefreshProjectInformation(); m_scene.setDropShadowEffect(false);
m_scene.nodeGeometry().setMarginsRatio(0.02);
m_view = new GraphicsView(&m_scene); m_view = new GraphicsView(&m_scene);
m_view->setScaleRange(0, 0); m_view->setScaleRange(0, 0);
@ -64,10 +64,9 @@ MainWindow::MainWindow()
setCentralWidget(m_view); setCentralWidget(m_view);
m_toolbar = new ToolBar(); m_toolbar = new ToolBar();
m_scene.setDropShadowEffect(false);
m_scene.nodeGeometry().setMarginsRatio(0.02);
m_toolbar->createActions(menuBar()); m_toolbar->createActions(menuBar());
addToolBar(m_toolbar); addToolBar(Qt::LeftToolBarArea, m_toolbar);
m_toolbar->setVisible(true);
connect(m_toolbar, &ToolBar::sigDefaultDocksPosition, this, &MainWindow::slotDefaultDocksPosition); connect(m_toolbar, &ToolBar::sigDefaultDocksPosition, this, &MainWindow::slotDefaultDocksPosition);
@ -118,6 +117,9 @@ MainWindow::MainWindow()
addDockWidget(Qt::DockWidgetArea::RightDockWidgetArea, m_romView); addDockWidget(Qt::DockWidgetArea::RightDockWidgetArea, m_romView);
m_toolbar->AddDockToMenu(m_romView->toggleViewAction()); m_toolbar->AddDockToMenu(m_romView->toggleViewAction());
tabifyDockWidget(m_vmDock, m_romView);
tabifyDockWidget(m_romView, m_ramView);
m_chooseFileDialog = new QDialog(this); m_chooseFileDialog = new QDialog(this);
m_chooseFileUi.setupUi(m_chooseFileDialog); m_chooseFileUi.setupUi(m_chooseFileDialog);
m_chooseFileDialog->close(); m_chooseFileDialog->close();
@ -167,8 +169,6 @@ MainWindow::MainWindow()
Callback<uint8_t(uint8_t)>::func = std::bind(&MainWindow::Syscall, this, std::placeholders::_1); Callback<uint8_t(uint8_t)>::func = std::bind(&MainWindow::Syscall, this, std::placeholders::_1);
m_chip32_ctx.syscall = static_cast<syscall_t>(Callback<uint8_t(uint8_t)>::callback); m_chip32_ctx.syscall = static_cast<syscall_t>(Callback<uint8_t(uint8_t)>::callback);
readSettings();
connect(m_toolbar, &ToolBar::sigNew, this, [&]() { connect(m_toolbar, &ToolBar::sigNew, this, [&]() {
NewProject(); NewProject();
}); });
@ -178,7 +178,7 @@ MainWindow::MainWindow()
}); });
connect(m_toolbar, &ToolBar::sigOpen, this, [&]() { connect(m_toolbar, &ToolBar::sigOpen, this, [&]() {
OpenProject(); OpenProjectDialog();
}); });
connect(m_toolbar, &ToolBar::sigClose, this, [&]() { connect(m_toolbar, &ToolBar::sigClose, this, [&]() {
@ -189,14 +189,21 @@ MainWindow::MainWindow()
ExitProgram(); ExitProgram();
}); });
connect(m_toolbar, &ToolBar::sigOpenRecent, this, [&](const QString &recent) {
CloseProject();
OpenProject(recent);
});
// Install event handler now that everythin is initialized // Install event handler now that everythin is initialized
Callback<void(QtMsgType , const QMessageLogContext &, const QString &)>::func = std::bind(&MainWindow::MessageOutput, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); Callback<void(QtMsgType , const QMessageLogContext &, const QString &)>::func = std::bind(&MainWindow::MessageOutput, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
auto cb = static_cast<message_output_t>(Callback<void(QtMsgType , const QMessageLogContext &, const QString &)>::callback); auto cb = static_cast<message_output_t>(Callback<void(QtMsgType , const QMessageLogContext &, const QString &)>::callback);
qInstallMessageHandler(cb); qInstallMessageHandler(cb);
qDebug() << "Started StoryTeller Editor"; readSettings();
qDebug() << "Settings location: " << m_settings.fileName();
qDebug() << "Welcome to StoryTeller Editor";
CloseProject(); CloseProject();
@ -216,26 +223,6 @@ void MainWindow::slotWelcome()
msgBox.exec(); msgBox.exec();
} }
void MainWindow::SetupTemporaryProject()
{
/*
QString appDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
// Look at any previous workspace used
// Generate a project unique ID name if no any
m_settings.setValue("project/workspace", m_project.uuid.c_str());
m_project.working_dir = QString(appDir + QDir::separator() + m_project.uuid.c_str()).toStdString();
m_project.name = "Untitled project";
m_project.Initialize(m_settings.value("project/workspace", QUuid::createUuid().toString()).toString().toStdString()
);
// m_resourcesDock->Initialize();
qDebug() << "Working dir is: " << m_project.working_dir.c_str();
*/
}
void MainWindow::ExitProgram() void MainWindow::ExitProgram()
{ {
// FIXME: warn if project not saved // FIXME: warn if project not saved
@ -243,7 +230,7 @@ void MainWindow::ExitProgram()
void MainWindow::RefreshProjectInformation() void MainWindow::RefreshProjectInformation()
{ {
setWindowTitle(QString("StoryTeller Editor - ") + m_project.GetWorkingDir().c_str()); setWindowTitle(QString("StoryTeller Editor - ") + m_project.GetProjectFilePath().c_str());
} }
void MainWindow::MessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) void MainWindow::MessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
@ -258,12 +245,20 @@ void MainWindow::readSettings()
restoreGeometry(m_settings.value("MainWindow/geometry").toByteArray()); restoreGeometry(m_settings.value("MainWindow/geometry").toByteArray());
restoreState(m_settings.value("MainWindow/windowState").toByteArray()); restoreState(m_settings.value("MainWindow/windowState").toByteArray());
// Restore recent projects list
m_recentProjects = m_settings.value("RecentProjects").toStringList();
m_toolbar->GenerateRecentProjectsMenu(m_recentProjects);
} }
void MainWindow::closeEvent(QCloseEvent *event) void MainWindow::closeEvent(QCloseEvent *event)
{ {
m_settings.setValue("MainWindow/geometry", saveGeometry()); m_settings.setValue("MainWindow/geometry", saveGeometry());
m_settings.setValue("MainWindow/windowState", saveState()); m_settings.setValue("MainWindow/windowState", saveState());
// Memorize recent projects list
m_settings.setValue("RecentProjects", m_recentProjects);
QMainWindow::closeEvent(event); QMainWindow::closeEvent(event);
} }
@ -485,11 +480,11 @@ void MainWindow::NewProject()
m_project.SetDisplayFormat(s.width(), s.height()); m_project.SetDisplayFormat(s.width(), s.height());
m_project.SetImageFormat(m_newProjectDialog->GetImageFormat()); m_project.SetImageFormat(m_newProjectDialog->GetImageFormat());
m_project.SetSoundFormat(m_newProjectDialog->GetSoundFormat()); m_project.SetSoundFormat(m_newProjectDialog->GetSoundFormat());
m_project.SetName(m_newProjectDialog->GetProjectName().toStdString());
m_project.SetUuid(QUuid::createUuid().toString().toStdString());
m_toolbar->SetAllDocks(true); SaveProject();
m_toolbar->SetActionsActive(true); EnableProject();
m_toolbar->ShowAllDocks(true);
m_view->setEnabled(true);
} }
} }
@ -497,51 +492,124 @@ void MainWindow::CloseProject()
{ {
m_project.Clear(); m_project.Clear();
m_toolbar->SetAllDocks(false); m_model.Clear();
m_ostHmiDock->Close();
m_resourcesDock->Close();
m_scriptEditorDock->Close();
m_vmDock->Close();
m_ramView->Close();
m_romView->Close();
m_logDock->Close();
m_toolbar->SetActionsActive(false); m_toolbar->SetActionsActive(false);
m_view->setEnabled(false); m_view->setEnabled(false);
} }
void MainWindow::OpenProject() void MainWindow::EnableProject()
{ {
QString fn = QFileDialog::getOpenFileName(this, tr("Open project file"), // Add to recent if not exists
QDir::homePath(), if (!m_recentProjects.contains(m_project.GetProjectFilePath().c_str()))
tr("StoryEditor Project (project.json)")); {
m_recentProjects.push_front(m_project.GetProjectFilePath().c_str());
// Limit to 10 recent projects
if (m_recentProjects.size() > 10) {
m_recentProjects.pop_back();
}
m_toolbar->GenerateRecentProjectsMenu(m_recentProjects);
}
m_project.Initialize(fn.toStdString()); m_ostHmiDock->Open();
m_resourcesDock->Open();
m_scriptEditorDock->Open();
m_vmDock->Open();
m_ramView->Open();
m_romView->Open();
m_logDock->Open();
m_toolbar->SetActionsActive(true);
m_view->setEnabled(true);
}
void MainWindow::OpenProjectDialog()
{
QFileDialog dialog(this);
dialog.setNameFilter(tr("StoryEditor Project (project.json)"));
dialog.setFileMode(QFileDialog::ExistingFile);
dialog.setDirectory(QDir::homePath());
if (dialog.exec())
{
QStringList fileNames = dialog.selectedFiles();
if (fileNames.size() == 1)
{
OpenProject(fileNames.at(0));
}
}
}
void MainWindow::OpenProject(const QString &filePath)
{
bool success = false;
QString errorMsg;
m_project.Initialize(filePath.toStdString());
QFile loadFile(m_project.GetProjectFilePath().c_str()); QFile loadFile(m_project.GetProjectFilePath().c_str());
if (!loadFile.open(QIODevice::ReadOnly)) { if (loadFile.open(QIODevice::ReadOnly))
qWarning("Couldn't open project file.");
return;
}
QJsonParseError err;
QJsonDocument loadDoc = QJsonDocument::fromJson(loadFile.readAll(), &err);
if (err.error == QJsonParseError::NoError)
{ {
QJsonObject projectRoot = loadDoc.object(); QJsonParseError err;
QString errorString; QJsonDocument loadDoc = QJsonDocument::fromJson(loadFile.readAll(), &err);
if (projectRoot.contains("project")) if (err.error == QJsonParseError::NoError)
{ {
QJsonObject projectData = projectRoot["project"].toObject(); QJsonObject projectRoot = loadDoc.object();
m_project.name = projectData["name"].toString().toStdString(); if (projectRoot.contains("project"))
// m_project.uuid = projectData["uuid"].toString().toStdString(); {
QJsonObject projectData = projectRoot["project"].toObject();
m_project.SetName(projectData["name"].toString().toStdString());
m_project.SetUuid(projectData["uuid"].toString().toStdString());
if (projectRoot.contains("nodegraph"))
{
QJsonObject nodeData = projectRoot["nodegraph"].toObject();
m_model.load(nodeData);
success = true;
}
else
{
errorMsg = tr("Missing nodegraph section in JSON file.");
}
}
else
{
errorMsg = tr("Missing project section in JSON file.");
}
} }
else
if (projectRoot.contains("nodegraph"))
{ {
QJsonObject nodeData = projectRoot["nodegraph"].toObject(); errorMsg = err.errorString();
m_model.load(nodeData);
} }
} }
else else
{ {
QMessageBox::critical(this, tr("Open project error"), err.errorString()); errorMsg = tr("Could not open project file.");
}
if (success)
{
EnableProject();
}
else
{
qWarning() << errorMsg;
QMessageBox::critical(this, tr("Open project error"), errorMsg);
} }
} }
@ -551,8 +619,8 @@ void MainWindow::SaveProject()
QJsonObject jsonModel = m_model.save(); QJsonObject jsonModel = m_model.save();
QJsonObject projectData; QJsonObject projectData;
projectData["name"] = m_project.name.c_str(); projectData["name"] = m_project.GetName().c_str();
// projectData["uuid"] = m_project.uuid.c_str(); projectData["uuid"] = m_project.GetUuid().c_str();
QJsonObject saveData; QJsonObject saveData;
saveData["project"] = projectData; saveData["project"] = projectData;
@ -572,12 +640,4 @@ void MainWindow::SaveProject()
statusBar()->showMessage(tr("Saved '%1'").arg(m_project.GetProjectFilePath().c_str()), 2000); statusBar()->showMessage(tr("Saved '%1'").arg(m_project.GetProjectFilePath().c_str()), 2000);
} }
void MainWindow::about()
{
QMessageBox::about(this, tr("About OST Editor"),
tr("OpenStoryTeller node editor."
"Build your own stories on an open source hardware."));
}

View file

@ -141,6 +141,7 @@ private:
QDialog *m_chooseFileDialog; QDialog *m_chooseFileDialog;
Ui::chooseFileDIalog m_chooseFileUi; Ui::chooseFileDIalog m_chooseFileUi;
NewProjectDialog *m_newProjectDialog{nullptr}; NewProjectDialog *m_newProjectDialog{nullptr};
QStringList m_recentProjects;
// VM // VM
uint8_t m_rom_data[16*1024]; uint8_t m_rom_data[16*1024];
@ -158,7 +159,6 @@ private:
void createStatusBar(); void createStatusBar();
void SaveProject(); void SaveProject();
void DisplayNode(StoryNode *m_tree, QtNodes::NodeId parentId); void DisplayNode(StoryNode *m_tree, QtNodes::NodeId parentId);
void about();
void buildScript(); void buildScript();
void highlightNextLine(); void highlightNextLine();
void readSettings(); void readSettings();
@ -170,11 +170,12 @@ private:
void MessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg); void MessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg);
void NewProject(); void NewProject();
void SetupTemporaryProject();
void RefreshProjectInformation(); void RefreshProjectInformation();
void CloseProject(); void CloseProject();
void OpenProject(); void OpenProjectDialog();
void ExitProgram(); void ExitProgram();
void EnableProject();
void OpenProject(const QString &filePath);
}; };
#endif // MAIN_WINDOW_H #endif // MAIN_WINDOW_H

View file

@ -2,7 +2,7 @@
#include "model/buffer/qmemorybuffer.h" #include "model/buffer/qmemorybuffer.h"
MemoryViewDock::MemoryViewDock(const QString &objectName, const QString &title) MemoryViewDock::MemoryViewDock(const QString &objectName, const QString &title)
: DockWidgetBase(title) : DockWidgetBase(title, false)
{ {
setObjectName(objectName); setObjectName(objectName);

View file

@ -33,7 +33,7 @@
<enum>QFrame::Box</enum> <enum>QFrame::Box</enum>
</property> </property>
<property name="text"> <property name="text">
<string>TextLabel</string> <string/>
</property> </property>
</widget> </widget>
</item> </item>

View file

@ -3,7 +3,7 @@
#include <QPixmap> #include <QPixmap>
OstHmiDock::OstHmiDock() OstHmiDock::OstHmiDock()
: DockWidgetBase(tr("StoryTeller HMI")) : DockWidgetBase(tr("StoryTeller HMI"), true)
{ {
setObjectName("OstHmiDock"); // used to save the state setObjectName("OstHmiDock"); // used to save the state
m_uiOstDisplay.setupUi(this); m_uiOstDisplay.setupUi(this);

View file

@ -4,7 +4,7 @@
ResourcesDock::ResourcesDock(StoryProject &project) ResourcesDock::ResourcesDock(StoryProject &project)
: m_project(project) : m_project(project)
, DockWidgetBase(tr("Resources")) , DockWidgetBase(tr("Resources"), true)
{ {
setObjectName("ResourcesDock"); // used to save the state setObjectName("ResourcesDock"); // used to save the state

View file

@ -1,114 +1,12 @@
#include "script_editor_dock.h" #include "script_editor_dock.h"
static const std::string test1 = R"(
; jump over the data, to our entry label
jump .entry
; Constant elements are separated by commas
$imageBird DC8, "example.bmp", 8 ; string of chars, followed by one byte
$soundChoice DC8, "choose1.snd"
$yaya DC8, "yaya.bmp"
$rabbit DC8, "rabbit.bmp"
$someConstant DC32, 12456789
; Liste des noeuds à appeler
$ChoiceObject DC32, 2, .MEDIA_02, .MEDIA_03
; DVsxx to declare a variable in RAM, followed by the number of elements
$MyArray DV8, 10 ; array of 10 bytes
$RamData1 DV32, 1 ; one 32-bit integer
$ChoiceMem DV32, 10 ; 10 elements for the choices, to be generated
; label definition
.entry: ;; comment here should work
; Syscall test: show image and play sound
lcons r0, $imageBird ; image name address in ROM located in R0 (null terminated)
lcons r1, $soundChoice ; set to 0 if no sound
syscall 1
lcons r0, $ChoiceObject
jump .media ; no return possible, so a jump is enough
; Generic media choice manager
.media:
; Les adresses des différents medias sont dans la stack
; Arguments:
; r0: address d'une structure de type "media choice"
; Local:
; t0: loop counter
; t1: increment 1
; t2: increment 4
; t3: current media address
.media_loop_start:
load t0, @r0, 4 ; Le premier élément est le nombre de choix possibles, t0 = 3 (exemple)
lcons t1, 1
lcons t2, 4
mov t3, r0
.media_loop:
add t3, t2 ; @++
; ------- On appelle un autre media node
push r0 ; save r0
load r0, @t3, 4 ; r0 = content in ram at address in T4
call r0
pop r0
; TODO: wait for event
sub t0, t1 ; i--
skipnz t0 ; if (r0) goto start_loop;
jump .media_loop_start
jump .media_loop
.MEDIA_02:
lcons r0, $yaya ; image name address in ROM located in R0 (null terminated)
lcons r1, $soundChoice ; set to 0 if no sound
syscall 1
ret
.MEDIA_03:
lcons r0, $rabbit
lcons r1, $soundChoice
syscall 1
ret
.SYSCALL_TEST:
; syscall test: wait for event
lcons r0, 0xFF ; wait for all event, blocking
syscall 2
; We create a stupid loop just for RAM variable testing
lcons r0, 4 ; prepare loop: 4 iterations
lcons r6, $RamData1 ; store address to R6
store @r6, r0, 4 ; save R0 in RAM
lcons r1, 1
.loop:
load r0, @r6, 4 ; load this variable
sub r0, r1
store @r6, r0, 4 ; save R0 in RAM
skipz r0 ; skip loop if R0 == 0
jump .loop
mov r0, r2 ; copy R2 into R0 (blank space between , and R2)
mov R0,R2 ; copy R2 into R0 (NO blank space between , and R2)
halt
)";
ScriptEditorDock::ScriptEditorDock() ScriptEditorDock::ScriptEditorDock()
: DockWidgetBase(tr("Script editor")) : DockWidgetBase(tr("Script editor"), false)
{ {
setObjectName("ScriptEditorDock"); // used to save the state setObjectName("ScriptEditorDock"); // used to save the state
m_editor = new CodeEditor(this); m_editor = new CodeEditor(this);
m_editor->setPlainText(test1.c_str());
m_highlighter = new Highlighter(m_editor->document()); m_highlighter = new Highlighter(m_editor->document());
setWidget(m_editor); setWidget(m_editor);
} }

View file

@ -310,11 +310,15 @@ bool StoryGraphModel::deleteNode(NodeId const nodeId)
QJsonObject StoryGraphModel::saveNode(NodeId const nodeId) const QJsonObject StoryGraphModel::saveNode(NodeId const nodeId) const
{ {
QJsonObject nodeJson; QJsonObject nodeJson;
nodeJson["id"] = static_cast<qint64>(nodeId); nodeJson["id"] = static_cast<qint64>(nodeId);
nodeJson["internal-data"] = _models.at(nodeId)->save(); auto it = _models.find(nodeId);
if (it == _models.end())
return nodeJson;
auto &model = it->second;
nodeJson["internal-data"] = model->save();
{ {
QPointF const pos = nodeData(nodeId, NodeRole::Position).value<QPointF>(); QPointF const pos = nodeData(nodeId, NodeRole::Position).value<QPointF>();
@ -352,27 +356,49 @@ QJsonObject StoryGraphModel::save() const
void StoryGraphModel::loadNode(QJsonObject const &nodeJson) void StoryGraphModel::loadNode(QJsonObject const &nodeJson)
{ {
NodeId restoredNodeId = static_cast<NodeId>(nodeJson["id"].toInt()); // Possibility of the id clash when reading it from json and not generating a
// new value.
// 1. When restoring a scene from a file.
// Conflict is not possible because the scene must be cleared by the time of
// loading.
// 2. When undoing the deletion command. Conflict is not possible
// because all the new ids were created past the removed nodes.
NodeId restoredNodeId = nodeJson["id"].toInt();
_nextNodeId = std::max(_nextNodeId, restoredNodeId + 1); _nextNodeId = std::max(_nextNodeId, restoredNodeId + 1);
// Create new node. QJsonObject const internalDataJson = nodeJson["internal-data"].toObject();
_nodeIds.insert(restoredNodeId);
setNodeData(restoredNodeId, NodeRole::InPortCount, nodeJson["inPortCount"].toString().toUInt()); QString delegateModelName = internalDataJson["model-name"].toString();
setNodeData(restoredNodeId, // std::unique_ptr<NodeDelegateModel> model = _registry->create(delegateModelName);
NodeRole::OutPortCount,
nodeJson["outPortCount"].toString().toUInt()); auto model = createNode(delegateModelName.toStdString());
if (model) {
// connect(model.get(),
// &NodeDelegateModel::dataUpdated,
// [restoredNodeId, this](PortIndex const portIndex) {
// onOutPortDataUpdated(restoredNodeId, portIndex);
// });
_models[restoredNodeId] = model;
model->setNodeId(restoredNodeId);
_nodeIds.insert(restoredNodeId);
Q_EMIT nodeCreated(restoredNodeId);
{
QJsonObject posJson = nodeJson["position"].toObject(); QJsonObject posJson = nodeJson["position"].toObject();
QPointF const pos(posJson["x"].toDouble(), posJson["y"].toDouble()); QPointF const pos(posJson["x"].toDouble(), posJson["y"].toDouble());
setNodeData(restoredNodeId, NodeRole::Position, pos); setNodeData(restoredNodeId, NodeRole::Position, pos);
}
Q_EMIT nodeCreated(restoredNodeId); _models[restoredNodeId]->load(internalDataJson);
} else {
throw std::logic_error(std::string("No registered model with name ")
+ delegateModelName.toLocal8Bit().data());
}
} }
void StoryGraphModel::load(QJsonObject const &jsonDocument) void StoryGraphModel::load(QJsonObject const &jsonDocument)
@ -419,3 +445,11 @@ void StoryGraphModel::removePort(NodeId nodeId, PortType portType, PortIndex por
portsDeleted(); portsDeleted();
} }
void StoryGraphModel::Clear()
{
_nodeIds.clear();
_connectivity.clear();
_models.clear();
emit modelReset();
}

View file

@ -131,6 +131,7 @@ public:
} }
StoryProject &GetProject() { return m_project; }; StoryProject &GetProject() { return m_project; };
void Clear();
signals: signals:
void sigChooseFile(NodeId id); void sigChooseFile(NodeId id);

View file

@ -25,6 +25,10 @@ public:
QPointF pos; QPointF pos;
}; };
~StoryNodeBase() {
std::cout << "Delete node: " << m_nodeId << std::endl;
}
void setNodeId(NodeId id) { m_nodeId = id; } void setNodeId(NodeId id) { m_nodeId = id; }
NodeId getNodeId() { return m_nodeId; } NodeId getNodeId() { return m_nodeId; }

View file

@ -101,7 +101,7 @@ bool StoryProject::Load(const std::string &file_path)
m_type = j["type"]; m_type = j["type"];
m_code = j["code"]; m_code = j["code"];
name = j["name"]; m_name = j["name"];
success = true; success = true;

View file

@ -47,8 +47,6 @@ struct StoryProject
enum ImageFormat { IMG_FORMAT_BMP_4BITS, IMG_FORMAT_QOIF, IMG_FORMAT_COUNT }; enum ImageFormat { IMG_FORMAT_BMP_4BITS, IMG_FORMAT_QOIF, IMG_FORMAT_COUNT };
enum SoundFormat { SND_FORMAT_WAV, SND_FORMAT_QOAF, SND_FORMAT_COUNT }; enum SoundFormat { SND_FORMAT_WAV, SND_FORMAT_QOAF, SND_FORMAT_COUNT };
// Project properties and location
std::string name; /// human readable name
std::vector<StoryNode> m_nodes; std::vector<StoryNode> m_nodes;
std::string m_type; std::string m_type;
@ -75,9 +73,13 @@ struct StoryProject
void SetImageFormat(ImageFormat format); void SetImageFormat(ImageFormat format);
void SetSoundFormat(SoundFormat format); void SetSoundFormat(SoundFormat format);
void SetDisplayFormat(int w, int h); void SetDisplayFormat(int w, int h);
void SetName(const std::string &name) { m_name = name; }
void SetUuid(const std::string &uuid) { m_uuid = uuid; }
std::string GetProjectFilePath() const; std::string GetProjectFilePath() const;
std::string GetWorkingDir() const; std::string GetWorkingDir() const;
std::string GetName() const { return m_name; }
std::string GetUuid() const { return m_uuid; }
std::filesystem::path ImagesPath() const { return m_imagesPath; } std::filesystem::path ImagesPath() const { return m_imagesPath; }
std::filesystem::path SoundsPath() const { return m_soundsPath; } std::filesystem::path SoundsPath() const { return m_soundsPath; }
@ -95,6 +97,8 @@ public:
static std::string ToUpper(const std::string &input); static std::string ToUpper(const std::string &input);
private: private:
// Project properties and location
std::string m_name; /// human readable name
std::string m_uuid; std::string m_uuid;
std::filesystem::path m_imagesPath; std::filesystem::path m_imagesPath;
std::filesystem::path m_soundsPath; std::filesystem::path m_soundsPath;

View file

@ -1,9 +1,14 @@
#include "toolbar.h" #include "toolbar.h"
#include "qmenubar.h" #include "qmenubar.h"
#include <QMessageBox>
ToolBar::ToolBar() ToolBar::ToolBar()
{ {
setObjectName("MainToolBar"); setObjectName("MainToolBar");
// setIconSize(QSize(10, 10));
// setFixedHeight(36);
} }
void ToolBar::createActions(QMenuBar *menuBar) void ToolBar::createActions(QMenuBar *menuBar)
@ -14,7 +19,7 @@ void ToolBar::createActions(QMenuBar *menuBar)
{ {
QIcon icon(":/assets/file-document-plus-outline.svg"); QIcon icon(":/assets/file-document-plus-outline.svg");
QAction *act = new QAction(icon, tr("&New project"), this); QAction *act = new QAction(icon, tr("&New project"), this);
act->setShortcuts(QKeySequence::Save); act->setShortcuts(QKeySequence::New);
act->setStatusTip(tr("Create a new project")); act->setStatusTip(tr("Create a new project"));
connect(act, &QAction::triggered, this, &ToolBar::sigNew); connect(act, &QAction::triggered, this, &ToolBar::sigNew);
fileMenu->addAction(act); fileMenu->addAction(act);
@ -51,6 +56,9 @@ void ToolBar::createActions(QMenuBar *menuBar)
addAction(m_closeProjectAction); addAction(m_closeProjectAction);
} }
m_recentProjectsMenu = new QMenu(tr("Recent projects"));
fileMenu->addMenu(m_recentProjectsMenu);
fileMenu->addSeparator(); fileMenu->addSeparator();
QAction *quitAct = fileMenu->addAction(tr("&Quit"), this, &ToolBar::sigExit); QAction *quitAct = fileMenu->addAction(tr("&Quit"), this, &ToolBar::sigExit);
@ -64,20 +72,27 @@ void ToolBar::createActions(QMenuBar *menuBar)
auto act = m_windowsMenu->addAction(tr("Reset docks position")); auto act = m_windowsMenu->addAction(tr("Reset docks position"));
connect(act, &QAction::triggered, this, &ToolBar::sigDefaultDocksPosition); connect(act, &QAction::triggered, this, &ToolBar::sigDefaultDocksPosition);
m_closeAllDocksAction = m_windowsMenu->addAction(tr("Show/Hide all docks"));
m_closeAllDocksAction->setCheckable(true);
connect(m_closeAllDocksAction, &QAction::triggered, this, [=] (bool checked) {
SetAllDocks(checked);
});
m_windowsMenu->addSeparator(); m_windowsMenu->addSeparator();
QMenu *helpMenu = menuBar->addMenu(tr("&Help")); QMenu *helpMenu = menuBar->addMenu(tr("&Help"));
QAction *aboutAct = helpMenu->addAction(tr("&About"), this, &ToolBar::sigAbout); QAction *aboutAct = helpMenu->addAction(tr("&About"), this, &ToolBar::slotAbout);
aboutAct->setStatusTip(tr("Show the application's About box")); aboutAct->setStatusTip(tr("Show the application's About box"));
} }
void ToolBar::GenerateRecentProjectsMenu(const QStringList &recents)
{
m_recentProjectsMenu->clear();
for (auto &r: recents)
{
auto act = m_recentProjectsMenu->addAction(r);
connect(act, &QAction::triggered, this, [&, r]() {
emit sigOpenRecent(r);
});
}
}
void ToolBar::SetActionsActive(bool enable) void ToolBar::SetActionsActive(bool enable)
{ {
for (auto d : m_actionDockList) for (auto d : m_actionDockList)
@ -89,19 +104,12 @@ void ToolBar::SetActionsActive(bool enable)
m_saveProjectAction->setEnabled(enable); m_saveProjectAction->setEnabled(enable);
} }
void ToolBar::ShowAllDocks(bool enable)
{
m_closeAllDocksAction->setEnabled(enable);
m_closeAllDocksAction->trigger();
}
void ToolBar::SetAllDocks(bool enable) void ToolBar::slotAbout()
{ {
for (auto d : m_actionDockList) QMessageBox::about(this, tr("About OST Editor"),
{ tr("OpenStoryTeller node editor."
d->setChecked(!enable); "Build your own stories on an open source hardware."));
d->trigger();
}
} }
void ToolBar::AddDockToMenu(QAction *action) void ToolBar::AddDockToMenu(QAction *action)

View file

@ -12,9 +12,8 @@ public:
ToolBar(); ToolBar();
void createActions(QMenuBar *menuBar); void createActions(QMenuBar *menuBar);
void AddDockToMenu(QAction *action); void AddDockToMenu(QAction *action);
void SetAllDocks(bool enable);
void SetActionsActive(bool enable); void SetActionsActive(bool enable);
void ShowAllDocks(bool enable); void GenerateRecentProjectsMenu(const QStringList &recents);
signals: signals:
void sigNew(); void sigNew();
@ -22,12 +21,15 @@ signals:
void sigOpen(); void sigOpen();
void sigClose(); void sigClose();
void sigExit(); void sigExit();
void sigAbout();
void sigDefaultDocksPosition(); void sigDefaultDocksPosition();
void sigOpenRecent(const QString &project);
private slots:
void slotAbout();
private: private:
QMenu *m_windowsMenu; QMenu *m_windowsMenu;
QAction *m_closeAllDocksAction; QMenu *m_recentProjectsMenu;
QAction *m_saveProjectAction; QAction *m_saveProjectAction;
QAction *m_closeProjectAction; QAction *m_closeProjectAction;
QList<QAction *> m_actionDockList; QList<QAction *> m_actionDockList;

View file

@ -1,7 +1,7 @@
#include "vm_dock.h" #include "vm_dock.h"
VmDock::VmDock(Chip32::Assembler &assembler) VmDock::VmDock(Chip32::Assembler &assembler)
: DockWidgetBase(tr("Virtual Machine")) : DockWidgetBase(tr("Virtual Machine"), false)
{ {
setObjectName("VirtualMachineDock"); // used to save the state setObjectName("VirtualMachineDock"); // used to save the state