New project dialog

This commit is contained in:
Anthony Rabine 2023-05-07 16:45:19 +02:00
parent d44946ad51
commit 85bf8d8e32
17 changed files with 487 additions and 208 deletions

View file

@ -13,18 +13,20 @@ Current status: ON DEVELOPMENT
## Bill of materials ## Bill of materials
The minimal list of components are: Here is an example of components.
- Links are examples of what to buy, price is not optimized
- Use traditional suppliers for generic components (buttons, resistors...), such as Adafruit, PiHut, Pimoroni, Kubii, Gotronic...
| Part | Price | Shop | | Part | Price | Shop | Link |
| ----------------------------------------- | -------- | --------- | | ------------------------------------- | -------- | --------- | ---------------------------------------- |
| Audio board + speaker | 13 € | Waveshare | | Audio board + speaker | 13 € | Waveshare | [Buy on Amazon](https://amzn.to/41nWgeB) |
| Raspberry Pico W | 9 € | Kubii | | Raspberry Pico W | 9 € | Kubii | [Buy on Amazon](https://amzn.to/3AUQeXQ) |
| 2inch LCD (320x240) | 14 € | Waveshare | | 2inch LCD (320x240) | 14 € | Waveshare | [Buy on Amazon](https://amzn.to/3LyG5oJ) |
| Some Pimoroni buttons are rotary switches | 4 € | Pimoroni | | Some push buttons and rotary switches | 4 € | Any | [Buy on Amazon](https://amzn.to/3AX6MOX) |
| UPS module or Pimoroni LiPo Shim | 15 € | Waveshare | | UPS module or Pimoroni LiPo Shim | 15 € | Waveshare | [Buy on Amazon](https://amzn.to/44p8Exo) |
| LiPo battery 500mAh | 9 € | Any | | LiPo battery 500mAh | 9 € | Any | [Buy on Amazon](https://amzn.to/3VCl3df) |
| Carte d'extension GPIO Pico Decker | 15 € | Waveshare | | Carte d'extension GPIO Pico 4 modules | 15 € | Waveshare | [Buy on Amazon](https://amzn.to/42ukJQ4) |
| **TOTAL** | **67 €** | | **TOTAL** | **67 €** |
In addition to this list, you may need some more materials such as wires, prototype boards, resistors... In addition to this list, you may need some more materials such as wires, prototype boards, resistors...

View file

@ -40,14 +40,14 @@ set(PROJECT_SOURCES
src/script_editor_dock.cpp src/script_editor_dock.cpp
src/memory_view_dock.h src/memory_view_dock.h
src/memory_view_dock.cpp src/memory_view_dock.cpp
src/nodeeditor_dock.h
src/nodeeditor_dock.cpp
src/osthmi_dock.h src/osthmi_dock.h
src/osthmi_dock.cpp src/osthmi_dock.cpp
src/log_dock.h src/log_dock.h
src/log_dock.cpp src/log_dock.cpp
src/vm_dock.h src/vm_dock.h
src/vm_dock.cpp src/vm_dock.cpp
src/new_project_dialog.h
src/new_project_dialog.cpp
src/dock_widget_base.h src/dock_widget_base.h
src/dock_widget_base.cpp src/dock_widget_base.cpp
src/code_editor.h src/code_editor.h

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3,16.74L7.76,12L3,7.26L7.26,3L12,7.76L16.74,3L21,7.26L16.24,12L21,16.74L16.74,21L12,16.24L7.26,21L3,16.74M12,13.41L16.74,18.16L18.16,16.74L13.41,12L18.16,7.26L16.74,5.84L12,10.59L7.26,5.84L5.84,7.26L10.59,12L5.84,16.74L7.26,18.16L12,13.41Z" /></svg>

After

Width:  |  Height:  |  Size: 319 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M6.1,10L4,18V8H21A2,2 0 0,0 19,6H12L10,4H4A2,2 0 0,0 2,6V18A2,2 0 0,0 4,20H19C19.9,20 20.7,19.4 20.9,18.5L23.2,10H6.1M19,18H6L7.6,12H20.6L19,18Z" /></svg>

After

Width:  |  Height:  |  Size: 223 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 KiB

View file

@ -24,6 +24,7 @@
#include <QtDebug> #include <QtDebug>
#include <QStandardPaths> #include <QStandardPaths>
#include <QDir> #include <QDir>
#include <QOpenGLWidget>
#include <QtNodes/BasicGraphicsScene> #include <QtNodes/BasicGraphicsScene>
#include <QtNodes/ConnectionStyle> #include <QtNodes/ConnectionStyle>
@ -50,31 +51,32 @@ 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")
{ {
SetupTemporaryProject(); m_project.Clear();
// SetupTemporaryProject();
RefreshProjectInformation(); // RefreshProjectInformation();
Callback<void(QtMsgType , const QMessageLogContext &, const QString &)>::func = std::bind(&MainWindow::MessageOutput, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); m_view = new GraphicsView(&m_scene);
auto cb = static_cast<message_output_t>(Callback<void(QtMsgType , const QMessageLogContext &, const QString &)>::callback); m_view->setScaleRange(0, 0);
m_view->setViewport(new QOpenGLWidget());
qInstallMessageHandler(cb); setCentralWidget(m_view);
m_toolbar = new ToolBar(); m_toolbar = new ToolBar();
m_scene.setDropShadowEffect(false); m_scene.setDropShadowEffect(false);
m_scene.nodeGeometry().setMarginsRatio(0.02); m_scene.nodeGeometry().setMarginsRatio(0.02);
m_toolbar->createActions(menuBar()); m_toolbar->createActions(menuBar());
addToolBar(m_toolbar); addToolBar(m_toolbar);
connect(m_toolbar, &ToolBar::sigDefaultDocksPosition, this, &MainWindow::slotDefaultDocksPosition);
createStatusBar(); createStatusBar();
m_logDock = new LogDock(); m_logDock = new LogDock();
addDockWidget(Qt::DockWidgetArea::BottomDockWidgetArea, m_logDock); addDockWidget(Qt::DockWidgetArea::BottomDockWidgetArea, m_logDock);
m_toolbar->AddDockToMenu(m_logDock->toggleViewAction()); m_toolbar->AddDockToMenu(m_logDock->toggleViewAction());
m_nodeEditorDock = new NodeEditorDock(&m_scene);
addDockWidget(Qt::DockWidgetArea::LeftDockWidgetArea, m_nodeEditorDock);
m_toolbar->AddDockToMenu(m_nodeEditorDock->toggleViewAction());
m_ostHmiDock = new OstHmiDock(); m_ostHmiDock = new OstHmiDock();
addDockWidget(Qt::DockWidgetArea::RightDockWidgetArea, m_ostHmiDock); addDockWidget(Qt::DockWidgetArea::RightDockWidgetArea, m_ostHmiDock);
m_toolbar->AddDockToMenu(m_ostHmiDock->toggleViewAction()); m_toolbar->AddDockToMenu(m_ostHmiDock->toggleViewAction());
@ -96,8 +98,6 @@ MainWindow::MainWindow()
m_toolbar->AddDockToMenu(m_vmDock->toggleViewAction()); m_toolbar->AddDockToMenu(m_vmDock->toggleViewAction());
connect(m_vmDock, &VmDock::sigCompile, [=]() { connect(m_vmDock, &VmDock::sigCompile, [=]() {
m_resourcesDock->SaveToProject(); m_resourcesDock->SaveToProject();
m_scriptEditorDock->setScript(m_project.Compile()); m_scriptEditorDock->setScript(m_project.Compile());
}); });
@ -140,6 +140,9 @@ MainWindow::MainWindow()
} }
}); });
m_newProjectDialog = new NewProjectDialog(this);
m_newProjectDialog->hide();
// TODO: merge both // TODO: merge both
m_model.registerNode<MediaNodeModel>("MediaNode"); m_model.registerNode<MediaNodeModel>("MediaNode");
m_model.addModel("MediaNode", "Story Teller"); m_model.addModel("MediaNode", "Story Teller");
@ -166,39 +169,81 @@ MainWindow::MainWindow()
readSettings(); readSettings();
connect(m_toolbar, &ToolBar::sigNew, this, [=]() { connect(m_toolbar, &ToolBar::sigNew, this, [&]() {
NewProject(); NewProject();
}); });
connect(m_toolbar, &ToolBar::sigSave, this, [=]() { connect(m_toolbar, &ToolBar::sigSave, this, [=]() {
SaveProject(); SaveProject();
}); });
connect(m_toolbar, &ToolBar::sigOpen, this, [&]() {
OpenProject();
});
connect(m_toolbar, &ToolBar::sigClose, this, [&]() {
CloseProject();
});
connect(m_toolbar, &ToolBar::sigExit, this, [&]() {
ExitProgram();
});
// 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);
auto cb = static_cast<message_output_t>(Callback<void(QtMsgType , const QMessageLogContext &, const QString &)>::callback);
qInstallMessageHandler(cb);
qDebug() << "Started StoryTeller Editor"; qDebug() << "Started StoryTeller Editor";
CloseProject();
// QMetaObject::invokeMethod(this, "slotWelcome", Qt::QueuedConnection);
} }
void MainWindow::CloseProject() void MainWindow::slotDefaultDocksPosition()
{ {
m_settings.clear();
m_settings.sync();
}
void MainWindow::slotWelcome()
{
QMessageBox msgBox;
msgBox.setText("Welcome to OpenStoryTeller Editor.\nCreate a new project or open an existing one.");
msgBox.exec();
} }
void MainWindow::SetupTemporaryProject() void MainWindow::SetupTemporaryProject()
{ {
/*
QString appDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); QString appDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
// Generate a project unique ID name // Look at any previous workspace used
m_project.uuid = QUuid::createUuid().toString().toStdString(); // 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.working_dir = QString(appDir + QDir::separator() + m_project.uuid.c_str()).toStdString();
m_project.Initialize(); m_project.name = "Untitled project";
m_project.Initialize(m_settings.value("project/workspace", QUuid::createUuid().toString()).toString().toStdString()
);
// m_resourcesDock->Initialize(); // m_resourcesDock->Initialize();
qDebug() << "Working dir is: " << m_project.working_dir.c_str(); qDebug() << "Working dir is: " << m_project.working_dir.c_str();
*/
}
void MainWindow::ExitProgram()
{
// FIXME: warn if project not saved
} }
void MainWindow::RefreshProjectInformation() void MainWindow::RefreshProjectInformation()
{ {
setWindowTitle(QString("StoryTeller Editor - ") + m_project.working_dir.c_str()); setWindowTitle(QString("StoryTeller Editor - ") + m_project.GetWorkingDir().c_str());
} }
void MainWindow::MessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) void MainWindow::MessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
@ -208,16 +253,17 @@ void MainWindow::MessageOutput(QtMsgType type, const QMessageLogContext &context
void MainWindow::readSettings() void MainWindow::readSettings()
{ {
QSettings settings("D8S", "OpenStoryTeller"); m_settings.setDefaultFormat(QSettings::IniFormat);
restoreGeometry(settings.value("MainWindow/geometry").toByteArray()); m_settings.setPath(QSettings::IniFormat, QSettings::UserScope, QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
restoreState(settings.value("MainWindow/windowState").toByteArray());
restoreGeometry(m_settings.value("MainWindow/geometry").toByteArray());
restoreState(m_settings.value("MainWindow/windowState").toByteArray());
} }
void MainWindow::closeEvent(QCloseEvent *event) void MainWindow::closeEvent(QCloseEvent *event)
{ {
QSettings settings("D8S", "OpenStoryTeller"); m_settings.setValue("MainWindow/geometry", saveGeometry());
settings.setValue("MainWindow/geometry", saveGeometry()); m_settings.setValue("MainWindow/windowState", saveState());
settings.setValue("MainWindow/windowState", saveState());
QMainWindow::closeEvent(event); QMainWindow::closeEvent(event);
} }
@ -227,7 +273,7 @@ void MainWindow::DisplayNode(StoryNode *m_tree, QtNodes::NodeId parentId)
if (m_tree->image >= 0) if (m_tree->image >= 0)
{ {
std::string imageFile = m_project.working_dir + "/rf/" + m_project.m_images[m_tree->image].file + ".bmp"; std::string imageFile = m_project.GetWorkingDir() + "/rf/" + m_project.m_images[m_tree->image].file + ".bmp";
std::cout << "Node image: " << imageFile << std::endl; std::cout << "Node image: " << imageFile << std::endl;
nodeInternalData["image"] = imageFile.c_str(); nodeInternalData["image"] = imageFile.c_str();
} }
@ -431,58 +477,99 @@ void MainWindow::createStatusBar()
void MainWindow::NewProject() void MainWindow::NewProject()
{ {
m_newProjectDialog->Initialize();
if (m_newProjectDialog->exec() == QDialog::Accepted)
{
m_project.Initialize(m_newProjectDialog->GetProjectFileName().toStdString());
QSize s = m_newProjectDialog->GetDisplayFormat();
m_project.SetDisplayFormat(s.width(), s.height());
m_project.SetImageFormat(m_newProjectDialog->GetImageFormat());
m_project.SetSoundFormat(m_newProjectDialog->GetSoundFormat());
m_toolbar->SetAllDocks(true);
m_toolbar->SetActionsActive(true);
m_toolbar->ShowAllDocks(true);
m_view->setEnabled(true);
}
} }
void MainWindow::CloseProject()
void MainWindow::open()
{ {
QMimeDatabase mimeDatabase; m_project.Clear();
QString fileName = QFileDialog::getOpenFileName(this,
tr("Choose a file name"), ".", m_toolbar->SetAllDocks(false);
mimeDatabase.mimeTypeForName("application/json").filterString()); m_toolbar->SetActionsActive(false);
if (fileName.isEmpty()) m_view->setEnabled(false);
return; }
QFile file(fileName);
if (!file.open(QFile::ReadOnly | QFile::Text)) { void MainWindow::OpenProject()
QMessageBox::warning(this, tr("Dock Widgets"), {
tr("Cannot write file %1:\n%2.") QString fn = QFileDialog::getOpenFileName(this, tr("Open project file"),
.arg(QDir::toNativeSeparators(fileName), file.errorString())); QDir::homePath(),
tr("StoryEditor Project (project.json)"));
m_project.Initialize(fn.toStdString());
QFile loadFile(m_project.GetProjectFilePath().c_str());
if (!loadFile.open(QIODevice::ReadOnly)) {
qWarning("Couldn't open project file.");
return; return;
} }
QTextStream in(&file); QJsonParseError err;
QGuiApplication::setOverrideCursor(Qt::WaitCursor); QJsonDocument loadDoc = QJsonDocument::fromJson(loadFile.readAll(), &err);
QJsonParseError error; if (err.error == QJsonParseError::NoError)
QString projectData = in.readAll(); {
QJsonObject projectRoot = loadDoc.object();
QString errorString;
QJsonDocument dataValue = QJsonDocument::fromJson(projectData.toUtf8(), &error); if (projectRoot.contains("project"))
{
QJsonObject projectData = projectRoot["project"].toObject();
m_model.load(dataValue.object()); m_project.name = projectData["name"].toString().toStdString();
// m_project.uuid = projectData["uuid"].toString().toStdString();
}
QGuiApplication::restoreOverrideCursor(); if (projectRoot.contains("nodegraph"))
{
QJsonObject nodeData = projectRoot["nodegraph"].toObject();
m_model.load(nodeData);
}
}
else
{
QMessageBox::critical(this, tr("Open project error"), err.errorString());
}
} }
void MainWindow::SaveProject() void MainWindow::SaveProject()
{ {
// Open the dialog if the project file does not exists QJsonObject jsonModel = m_model.save();
if (!QFile::exists(m_project.file_path.c_str()))
{ QJsonObject projectData;
// Save current project projectData["name"] = m_project.name.c_str();
QString fileName = QFileDialog::getSaveFileName(this, tr("Save project file"), // projectData["uuid"] = m_project.uuid.c_str();
QDir::homePath() + "/new_story.ostproj",
tr("OpenStory Teller project (*.ostproj)")); QJsonObject saveData;
if (fileName.isEmpty()) saveData["project"] = projectData;
return; saveData["nodegraph"] = jsonModel;
m_project.file_path = fileName.toStdString();
}
QJsonObject saveData = m_model.save();
QJsonDocument doc(saveData); QJsonDocument doc(saveData);
qDebug() << doc.toJson(); qDebug() << doc.toJson();
statusBar()->showMessage(tr("Saved '%1'").arg(m_project.file_path.c_str()), 2000); QFile f(m_project.GetProjectFilePath().c_str());
if (!f.open(QIODevice::WriteOnly | QIODevice::Text))
return;
QTextStream fout(&f);
fout << doc.toJson();
statusBar()->showMessage(tr("Saved '%1'").arg(m_project.GetProjectFilePath().c_str()), 2000);
} }
void MainWindow::about() void MainWindow::about()

View file

@ -11,9 +11,9 @@
#include <QMainWindow> #include <QMainWindow>
#include <QDockWidget> #include <QDockWidget>
#include <QtNodes/ConnectionStyle> #include <QtNodes/ConnectionStyle>
#include <QtNodes/GraphicsView>
#include <QtNodes/BasicGraphicsScene>
#include <QtNodes/StyleCollection> #include <QtNodes/StyleCollection>
#include <QtNodes/DataFlowGraphModel> #include <QtNodes/DataFlowGraphModel>
#include <QtNodes/DataFlowGraphicsScene> #include <QtNodes/DataFlowGraphicsScene>
@ -23,8 +23,8 @@
using QtNodes::DataFlowGraphicsScene; using QtNodes::DataFlowGraphicsScene;
using QtNodes::ConnectionStyle; using QtNodes::ConnectionStyle;
using QtNodes::BasicGraphicsScene;
using QtNodes::GraphicsView;
using QtNodes::NodeRole; using QtNodes::NodeRole;
using QtNodes::StyleCollection; using QtNodes::StyleCollection;
using QtNodes::DataFlowGraphModel; using QtNodes::DataFlowGraphModel;
@ -41,9 +41,10 @@ using QtNodes::NodeDelegateModelRegistry;
#include "script_editor_dock.h" #include "script_editor_dock.h"
#include "memory_view_dock.h" #include "memory_view_dock.h"
#include "osthmi_dock.h" #include "osthmi_dock.h"
#include "nodeeditor_dock.h"
#include "log_dock.h" #include "log_dock.h"
#include "toolbar.h" #include "toolbar.h"
#include "new_project_dialog.h"
struct DebugContext struct DebugContext
{ {
@ -118,15 +119,17 @@ public:
private slots: private slots:
void stepInstruction(); void stepInstruction();
void closeEvent(QCloseEvent *event); void closeEvent(QCloseEvent *event);
void slotDefaultDocksPosition();
void slotWelcome();
private: private:
StoryProject m_project; StoryProject m_project;
StoryGraphModel m_model; StoryGraphModel m_model;
StoryGraphScene m_scene; StoryGraphScene m_scene;
GraphicsView *m_view{nullptr};
// Qt stuff // Qt stuff
ToolBar *m_toolbar{nullptr}; ToolBar *m_toolbar{nullptr};
NodeEditorDock *m_nodeEditorDock{nullptr};
OstHmiDock *m_ostHmiDock{nullptr}; OstHmiDock *m_ostHmiDock{nullptr};
ResourcesDock *m_resourcesDock{nullptr}; ResourcesDock *m_resourcesDock{nullptr};
ScriptEditorDock *m_scriptEditorDock{nullptr}; ScriptEditorDock *m_scriptEditorDock{nullptr};
@ -134,9 +137,10 @@ private:
MemoryViewDock *m_ramView{nullptr}; MemoryViewDock *m_ramView{nullptr};
MemoryViewDock *m_romView{nullptr}; MemoryViewDock *m_romView{nullptr};
LogDock *m_logDock{nullptr}; LogDock *m_logDock{nullptr};
QSettings m_settings;
QDialog *m_chooseFileDialog; QDialog *m_chooseFileDialog;
Ui::chooseFileDIalog m_chooseFileUi; Ui::chooseFileDIalog m_chooseFileUi;
NewProjectDialog *m_newProjectDialog{nullptr};
// VM // VM
uint8_t m_rom_data[16*1024]; uint8_t m_rom_data[16*1024];
@ -155,7 +159,6 @@ private:
void SaveProject(); void SaveProject();
void DisplayNode(StoryNode *m_tree, QtNodes::NodeId parentId); void DisplayNode(StoryNode *m_tree, QtNodes::NodeId parentId);
void about(); void about();
void open();
void buildScript(); void buildScript();
void highlightNextLine(); void highlightNextLine();
void readSettings(); void readSettings();
@ -170,6 +173,8 @@ private:
void SetupTemporaryProject(); void SetupTemporaryProject();
void RefreshProjectInformation(); void RefreshProjectInformation();
void CloseProject(); void CloseProject();
void OpenProject();
void ExitProgram();
}; };
#endif // MAIN_WINDOW_H #endif // MAIN_WINDOW_H

View file

@ -1,23 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0"> <ui version="4.0">
<class>NewProjectDialog</class> <class>newProjectDialog</class>
<widget class="QDialog" name="NewProjectDialog"> <widget class="QDialog" name="newProjectDialog">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>346</width> <width>346</width>
<height>263</height> <height>269</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Dialog</string> <string>New project</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QGroupBox" name="groupBox"> <widget class="QGroupBox" name="groupBox">
<property name="title"> <property name="title">
<string>New project parameters</string> <string>New project parameters (directory must be empty)</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_2"> <layout class="QVBoxLayout" name="verticalLayout_2">
<item> <item>
@ -30,10 +30,14 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QLineEdit" name="lineEdit"/> <widget class="QLineEdit" name="directoryPath">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="pushButton"> <widget class="QPushButton" name="selectDirectoryButton">
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>40</width> <width>40</width>
@ -63,7 +67,7 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QLineEdit" name="lineEdit_2"/> <widget class="QLineEdit" name="projectName"/>
</item> </item>
</layout> </layout>
</item> </item>
@ -77,13 +81,7 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QComboBox" name="comboBox"> <widget class="QComboBox" name="displaySizeCombo"/>
<item>
<property name="text">
<string>320x240</string>
</property>
</item>
</widget>
</item> </item>
</layout> </layout>
</item> </item>
@ -97,13 +95,7 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QComboBox" name="comboBox_2"> <widget class="QComboBox" name="imageFormatCombo"/>
<item>
<property name="text">
<string>BMP (compressed 4-bit palette)</string>
</property>
</item>
</widget>
</item> </item>
</layout> </layout>
</item> </item>
@ -117,13 +109,7 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QComboBox" name="comboBox_3"> <widget class="QComboBox" name="soundFormatCombo"/>
<item>
<property name="text">
<string>QOA (Quite Ok Audio)</string>
</property>
</item>
</widget>
</item> </item>
</layout> </layout>
</item> </item>
@ -140,6 +126,16 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item>
<widget class="QLabel" name="errorMessage">
<property name="text">
<string>ErrorMessage</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
@ -156,38 +152,5 @@
</layout> </layout>
</widget> </widget>
<resources/> <resources/>
<connections> <connections/>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>NewProjectDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>NewProjectDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui> </ui>

View file

@ -0,0 +1,106 @@
#include "new_project_dialog.h"
#include <QFileDialog>
#include <QDir>
NewProjectDialog::NewProjectDialog(QWidget *parent)
: QDialog{parent}
{
m_ui.setupUi(this);
m_ui.imageFormatCombo->addItem(tr("BMP (compressed 4-bit palette)"), StoryProject::IMG_FORMAT_BMP_4BITS);
m_ui.imageFormatCombo->addItem(tr("QOIF (Quite Ok Image Format)"), StoryProject::IMG_FORMAT_QOIF);
m_ui.soundFormatCombo->addItem(tr("WAV"), StoryProject::SND_FORMAT_WAV);
m_ui.soundFormatCombo->addItem(tr("QOA (Quite Ok Audio)"), StoryProject::SND_FORMAT_QOAF);
m_ui.displaySizeCombo->addItem("320x240", QSize(320,240));
connect(m_ui.selectDirectoryButton, &QPushButton::clicked, this, [&]() {
m_dir = QFileDialog::getExistingDirectory(this, tr("Project Directory"),
QDir::homePath(),
QFileDialog::ShowDirsOnly
| QFileDialog::DontResolveSymlinks);
m_ui.directoryPath->setText(m_dir);
});
connect(m_ui.buttonBox, &QDialogButtonBox::accepted, this, [&]() {
QDir projDir(m_dir);
if (projDir.exists())
{
// Le répertoire doit être vide
bool empty = projDir.entryInfoList(QDir::NoDotAndDotDot|QDir::AllEntries).count() == 0;
if (empty)
{
// Test project name
if (!m_ui.projectName->text().isEmpty())
{
accept();
}
else
{
m_ui.errorMessage->setText(R"(<p style="color:red">)" + tr("Error, project name empty.") + R"(</p>)");
}
}
else
{
m_ui.errorMessage->setText(R"(<p style="color:red">)" + tr("Error, directory is not empty.") + R"(</p>)");
}
}
else
{
m_ui.errorMessage->setText(R"(<p style="color:red">)" + tr("Error, directory does not exist.") + R"(</p>)");
}
});
connect(m_ui.buttonBox, &QDialogButtonBox::rejected, this, [&]() {
this->close();
});
}
QString NewProjectDialog::GetProjectFileName() const
{
return m_dir + QDir::separator() + "project.json";
}
QString NewProjectDialog::GetProjectName() const { return m_ui.projectName->text(); }
StoryProject::ImageFormat NewProjectDialog::GetImageFormat() const
{
StoryProject::ImageFormat img{StoryProject::IMG_FORMAT_BMP_4BITS};
int idx = m_ui.imageFormatCombo->currentData().toInt();
if (idx < StoryProject::IMG_FORMAT_COUNT) {
img = static_cast<StoryProject::ImageFormat>(idx);
}
return img;
}
StoryProject::SoundFormat NewProjectDialog::GetSoundFormat() const
{
StoryProject::SoundFormat img{StoryProject::SND_FORMAT_WAV};
int idx = m_ui.soundFormatCombo->currentData().toInt();
if (idx < StoryProject::IMG_FORMAT_COUNT) {
img = static_cast<StoryProject::SoundFormat>(idx);
}
return img;
}
QSize NewProjectDialog::GetDisplayFormat() const
{
return m_ui.displaySizeCombo->currentData().toSize();
}
void NewProjectDialog::Initialize()
{
m_ui.errorMessage->clear();
m_ui.directoryPath->clear();
}

View file

@ -0,0 +1,32 @@
#ifndef NEW_PROJECT_DIALOG_H
#define NEW_PROJECT_DIALOG_H
#include <QObject>
#include <QDialog>
#include "ui_new-project.h"
#include "story_project.h"
class NewProjectDialog : public QDialog
{
Q_OBJECT
public:
explicit NewProjectDialog(QWidget *parent = nullptr);
QString GetProjectFileName() const;
QString GetProjectName() const;
StoryProject::ImageFormat GetImageFormat() const;
StoryProject::SoundFormat GetSoundFormat() const;
QSize GetDisplayFormat() const;
void Initialize();
signals:
void sigAccepted();
private:
Ui::newProjectDialog m_ui;
QString m_dir;
};
#endif // NEW_PROJECT_DIALOG_H

View file

@ -1,15 +0,0 @@
#include "nodeeditor_dock.h"
#include <QOpenGLWidget>
NodeEditorDock::NodeEditorDock(BasicGraphicsScene *scene)
: DockWidgetBase(tr("Node editor"))
{
setObjectName("NodeEditorDock");
m_view = new GraphicsView(scene);
m_view->setScaleRange(0, 0);
m_view->setViewport(new QOpenGLWidget());
setAllowedAreas(Qt::AllDockWidgetAreas);
setWidget(m_view);
}

View file

@ -1,20 +0,0 @@
#ifndef NODEEDITORDOCK_H
#define NODEEDITORDOCK_H
#include "dock_widget_base.h"
#include <QtNodes/GraphicsView>
#include <QtNodes/BasicGraphicsScene>
using QtNodes::BasicGraphicsScene;
using QtNodes::GraphicsView;
class NodeEditorDock : public DockWidgetBase
{
public:
NodeEditorDock(BasicGraphicsScene *scene);
private:
GraphicsView *m_view{nullptr};
};
#endif // NODEEDITORDOCK_H

View file

@ -6,5 +6,8 @@
<file>../assets/build.png</file> <file>../assets/build.png</file>
<file>../assets/volume-off.png</file> <file>../assets/volume-off.png</file>
<file>../assets/file-document-plus-outline.svg</file> <file>../assets/file-document-plus-outline.svg</file>
<file>../assets/close-outline.svg</file>
<file>../assets/folder-open-outline.svg</file>
<file>../assets/welcome.png</file>
</qresource> </qresource>
</RCC> </RCC>

View file

@ -6,19 +6,31 @@
#include <filesystem> #include <filesystem>
#include "json.hpp" #include "json.hpp"
void StoryProject::New(const std::string &uuid, const std::string &file_path)
void StoryProject::Initialize()
{ {
m_uuid = uuid;
Initialize(file_path);
}
void StoryProject::Initialize(const std::string &file_path)
{
m_project_path = file_path;
std::filesystem::path p(file_path);
m_working_dir= p.parent_path();
// Frist try to create the working directory // Frist try to create the working directory
if (!std::filesystem::is_directory(working_dir)) if (!std::filesystem::is_directory(m_working_dir))
{ {
std::filesystem::create_directories(working_dir); std::filesystem::create_directories(m_working_dir);
} }
m_imagesPath = std::filesystem::path(working_dir) / "images"; m_imagesPath = std::filesystem::path(m_working_dir) / "images";
m_soundsPath = std::filesystem::path(working_dir) / "sounds"; m_soundsPath = std::filesystem::path(m_working_dir) / "sounds";
std::filesystem::create_directories(m_imagesPath); std::filesystem::create_directories(m_imagesPath);
std::filesystem::create_directories(m_soundsPath); std::filesystem::create_directories(m_soundsPath);
m_initialized = true;
} }
bool StoryProject::Load(const std::string &file_path) bool StoryProject::Load(const std::string &file_path)
@ -27,9 +39,9 @@ bool StoryProject::Load(const std::string &file_path)
bool success = false; bool success = false;
std::filesystem::path p(file_path); std::filesystem::path p(file_path);
working_dir= p.parent_path(); m_working_dir= p.parent_path();
std::cout << "Working dir is: " << working_dir << std::endl; std::cout << "Working dir is: " << m_working_dir << std::endl;
try { try {
@ -89,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"];
m_name = j["name"]; name = j["name"];
success = true; success = true;
@ -207,3 +219,29 @@ std::string StoryProject::Compile()
return chip32.str(); return chip32.str();
} }
void StoryProject::SetImageFormat(ImageFormat format)
{
m_imageFormat = format;
}
void StoryProject::SetSoundFormat(SoundFormat format)
{
m_soundFormat = format;
}
void StoryProject::SetDisplayFormat(int w, int h)
{
m_display_w = w;
m_display_h = h;
}
std::string StoryProject::GetProjectFilePath() const
{
return m_project_path;
}
std::string StoryProject::GetWorkingDir() const
{
return m_working_dir;
}

View file

@ -44,36 +44,40 @@ struct Resource
struct StoryProject struct StoryProject
{ {
std::string uuid; enum ImageFormat { IMG_FORMAT_BMP_4BITS, IMG_FORMAT_QOIF, IMG_FORMAT_COUNT };
std::string working_dir; /// Temporary folder based on the uuid, where the archive is unzipped enum SoundFormat { SND_FORMAT_WAV, SND_FORMAT_QOAF, SND_FORMAT_COUNT };
std::string file_path; /// project file (archive)
// 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;
std::string m_code; std::string m_code;
std::string m_name;
std::vector<Resource> m_images; std::vector<Resource> m_images;
std::vector<Resource> m_sounds; std::vector<Resource> m_sounds;
StoryNode *m_tree; StoryNode *m_tree;
// Initialize a project
// The following parameters must be set before calling this method:
// - uuid
// - working_directory
void Initialize();
bool Load(const std::string &file_path); bool Load(const std::string &file_path);
void CreateTree(); void CreateTree();
void Clear() { void Clear() {
m_uuid = "";
m_working_dir = "";
m_images.clear(); m_images.clear();
m_sounds.clear(); m_sounds.clear();
m_initialized = false;
} }
std::string Compile(); std::string Compile();
void SetImageFormat(ImageFormat format);
void SetSoundFormat(SoundFormat format);
void SetDisplayFormat(int w, int h);
std::string GetProjectFilePath() const;
std::string GetWorkingDir() const;
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; }
@ -83,11 +87,27 @@ struct StoryProject
static void ReplaceCharacter(std::string &theString, const std::string &toFind, const std::string &toReplace); static void ReplaceCharacter(std::string &theString, const std::string &toFind, const std::string &toReplace);
public: public:
// Initialize with an existing project
void Initialize(const std::string &file_path);
void New(const std::string &uuid, const std::string &file_path);
static void EraseString(std::string &theString, const std::string &toErase); static void EraseString(std::string &theString, const std::string &toErase);
static std::string ToUpper(const std::string &input); static std::string ToUpper(const std::string &input);
private: private:
std::string m_uuid;
std::filesystem::path m_imagesPath; std::filesystem::path m_imagesPath;
std::filesystem::path m_soundsPath; std::filesystem::path m_soundsPath;
bool m_initialized{false};
std::string m_working_dir; /// Temporary folder based on the uuid, where the archive is unzipped
std::string m_project_path; /// JSON project file
int m_display_w{320};
int m_display_h{240};
ImageFormat m_imageFormat{IMG_FORMAT_BMP_4BITS};
SoundFormat m_soundFormat{SND_FORMAT_WAV};
}; };
#endif // STORY_PROJECT_H #endif // STORY_PROJECT_H

View file

@ -12,7 +12,7 @@ void ToolBar::createActions(QMenuBar *menuBar)
// ------------ New // ------------ New
{ {
const QIcon icon = QIcon::fromTheme("document-save", QIcon(":/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::Save);
act->setStatusTip(tr("Create a new project")); act->setStatusTip(tr("Create a new project"));
@ -20,20 +20,19 @@ void ToolBar::createActions(QMenuBar *menuBar)
fileMenu->addAction(act); fileMenu->addAction(act);
addAction(act); addAction(act);
} }
// ------------ Save // ------------ Save
{ {
const QIcon icon = QIcon::fromTheme("document-save", QIcon(":/assets/floppy.svg")); QIcon icon(":/assets/floppy.svg");
QAction *act = new QAction(icon, tr("&Save project"), this); m_saveProjectAction = new QAction(icon, tr("&Save project"), this);
act->setShortcuts(QKeySequence::Save); m_saveProjectAction->setShortcuts(QKeySequence::Save);
act->setStatusTip(tr("Save the current project")); m_saveProjectAction->setStatusTip(tr("Save the current project"));
connect(act, &QAction::triggered, this, &ToolBar::sigSave); connect(m_saveProjectAction, &QAction::triggered, this, &ToolBar::sigSave);
fileMenu->addAction(act); fileMenu->addAction(m_saveProjectAction);
addAction(act); addAction(m_saveProjectAction);
} }
// ------------ Open // ------------ Open
{ {
const QIcon icon = QIcon::fromTheme("document-open", QIcon(":/assets/folder-open.svg")); QIcon icon(":/assets/folder-open-outline.svg");
QAction *act = new QAction(icon, tr("&Open project"), this); QAction *act = new QAction(icon, tr("&Open project"), this);
act->setShortcuts(QKeySequence::Open); act->setShortcuts(QKeySequence::Open);
act->setStatusTip(tr("Open an existing project")); act->setStatusTip(tr("Open an existing project"));
@ -41,10 +40,20 @@ void ToolBar::createActions(QMenuBar *menuBar)
fileMenu->addAction(act); fileMenu->addAction(act);
addAction(act); addAction(act);
} }
// ------------ Close
{
QIcon icon(":/assets/close-outline.svg");
m_closeProjectAction = new QAction(icon, tr("&Close project"), this);
m_closeProjectAction->setShortcuts(QKeySequence::Close);
m_closeProjectAction->setStatusTip(tr("Close current project"));
connect(m_closeProjectAction, &QAction::triggered, this, &ToolBar::sigClose);
fileMenu->addAction(m_closeProjectAction);
addAction(m_closeProjectAction);
}
fileMenu->addSeparator(); fileMenu->addSeparator();
QAction *quitAct = fileMenu->addAction(tr("&Quit"), this, &QWidget::close); QAction *quitAct = fileMenu->addAction(tr("&Quit"), this, &ToolBar::sigExit);
quitAct->setShortcuts(QKeySequence::Quit); quitAct->setShortcuts(QKeySequence::Quit);
quitAct->setStatusTip(tr("Quit the application")); quitAct->setStatusTip(tr("Quit the application"));
@ -52,13 +61,51 @@ void ToolBar::createActions(QMenuBar *menuBar)
m_windowsMenu = menuBar->addMenu(tr("&Windows")); m_windowsMenu = menuBar->addMenu(tr("&Windows"));
auto act = m_windowsMenu->addAction(tr("Reset docks position"));
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();
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::sigAbout);
aboutAct->setStatusTip(tr("Show the application's About box")); aboutAct->setStatusTip(tr("Show the application's About box"));
} }
void ToolBar::SetActionsActive(bool enable)
{
for (auto d : m_actionDockList)
{
d->setEnabled(enable);
}
m_windowsMenu->setEnabled(enable);
m_closeProjectAction->setEnabled(enable);
m_saveProjectAction->setEnabled(enable);
}
void ToolBar::ShowAllDocks(bool enable)
{
m_closeAllDocksAction->setEnabled(enable);
m_closeAllDocksAction->trigger();
}
void ToolBar::SetAllDocks(bool enable)
{
for (auto d : m_actionDockList)
{
d->setChecked(!enable);
d->trigger();
}
}
void ToolBar::AddDockToMenu(QAction *action) void ToolBar::AddDockToMenu(QAction *action)
{ {
m_windowsMenu->addAction(action); m_windowsMenu->addAction(action);
m_actionDockList.push_back(action);
} }

View file

@ -11,17 +11,26 @@ class ToolBar : public QToolBar
public: 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 ShowAllDocks(bool enable);
signals: signals:
void sigNew(); void sigNew();
void sigSave(); void sigSave();
void sigOpen(); void sigOpen();
void sigClose();
void sigExit();
void sigAbout(); void sigAbout();
void sigDefaultDocksPosition();
private: private:
QMenu *m_windowsMenu; QMenu *m_windowsMenu;
QAction *m_closeAllDocksAction;
QAction *m_saveProjectAction;
QAction *m_closeProjectAction;
QList<QAction *> m_actionDockList;
}; };
#endif // TOOLBAR_H #endif // TOOLBAR_H