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,17 +13,19 @@ Current status: ON DEVELOPMENT
## 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 |
| ----------------------------------------- | -------- | --------- |
| Audio board + speaker | 13 € | Waveshare |
| Raspberry Pico W | 9 € | Kubii |
| 2inch LCD (320x240) | 14 € | Waveshare |
| Some Pimoroni buttons are rotary switches | 4 € | Pimoroni |
| UPS module or Pimoroni LiPo Shim | 15 € | Waveshare |
| LiPo battery 500mAh | 9 € | Any |
| Carte d'extension GPIO Pico Decker | 15 € | Waveshare |
| Part | Price | Shop | Link |
| ------------------------------------- | -------- | --------- | ---------------------------------------- |
| Audio board + speaker | 13 € | Waveshare | [Buy on Amazon](https://amzn.to/41nWgeB) |
| Raspberry Pico W | 9 € | Kubii | [Buy on Amazon](https://amzn.to/3AUQeXQ) |
| 2inch LCD (320x240) | 14 € | Waveshare | [Buy on Amazon](https://amzn.to/3LyG5oJ) |
| Some push buttons and rotary switches | 4 € | Any | [Buy on Amazon](https://amzn.to/3AX6MOX) |
| UPS module or Pimoroni LiPo Shim | 15 € | Waveshare | [Buy on Amazon](https://amzn.to/44p8Exo) |
| LiPo battery 500mAh | 9 € | Any | [Buy on Amazon](https://amzn.to/3VCl3df) |
| Carte d'extension GPIO Pico 4 modules | 15 € | Waveshare | [Buy on Amazon](https://amzn.to/42ukJQ4) |
| **TOTAL** | **67 €** |
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/memory_view_dock.h
src/memory_view_dock.cpp
src/nodeeditor_dock.h
src/nodeeditor_dock.cpp
src/osthmi_dock.h
src/osthmi_dock.cpp
src/log_dock.h
src/log_dock.cpp
src/vm_dock.h
src/vm_dock.cpp
src/new_project_dialog.h
src/new_project_dialog.cpp
src/dock_widget_base.h
src/dock_widget_base.cpp
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 <QStandardPaths>
#include <QDir>
#include <QOpenGLWidget>
#include <QtNodes/BasicGraphicsScene>
#include <QtNodes/ConnectionStyle>
@ -50,31 +51,32 @@ typedef void (*message_output_t)(QtMsgType , const QMessageLogContext &, const Q
MainWindow::MainWindow()
: m_model(m_project)
, 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);
auto cb = static_cast<message_output_t>(Callback<void(QtMsgType , const QMessageLogContext &, const QString &)>::callback);
qInstallMessageHandler(cb);
m_view = new GraphicsView(&m_scene);
m_view->setScaleRange(0, 0);
m_view->setViewport(new QOpenGLWidget());
setCentralWidget(m_view);
m_toolbar = new ToolBar();
m_scene.setDropShadowEffect(false);
m_scene.nodeGeometry().setMarginsRatio(0.02);
m_toolbar->createActions(menuBar());
addToolBar(m_toolbar);
connect(m_toolbar, &ToolBar::sigDefaultDocksPosition, this, &MainWindow::slotDefaultDocksPosition);
createStatusBar();
m_logDock = new LogDock();
addDockWidget(Qt::DockWidgetArea::BottomDockWidgetArea, m_logDock);
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();
addDockWidget(Qt::DockWidgetArea::RightDockWidgetArea, m_ostHmiDock);
m_toolbar->AddDockToMenu(m_ostHmiDock->toggleViewAction());
@ -96,8 +98,6 @@ MainWindow::MainWindow()
m_toolbar->AddDockToMenu(m_vmDock->toggleViewAction());
connect(m_vmDock, &VmDock::sigCompile, [=]() {
m_resourcesDock->SaveToProject();
m_scriptEditorDock->setScript(m_project.Compile());
});
@ -140,6 +140,9 @@ MainWindow::MainWindow()
}
});
m_newProjectDialog = new NewProjectDialog(this);
m_newProjectDialog->hide();
// TODO: merge both
m_model.registerNode<MediaNodeModel>("MediaNode");
m_model.addModel("MediaNode", "Story Teller");
@ -166,7 +169,7 @@ MainWindow::MainWindow()
readSettings();
connect(m_toolbar, &ToolBar::sigNew, this, [=]() {
connect(m_toolbar, &ToolBar::sigNew, this, [&]() {
NewProject();
});
@ -174,31 +177,73 @@ MainWindow::MainWindow()
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";
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()
{
/*
QString appDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
// Generate a project unique ID name
m_project.uuid = QUuid::createUuid().toString().toStdString();
// 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.Initialize();
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()
{
// FIXME: warn if project not saved
}
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)
@ -208,16 +253,17 @@ void MainWindow::MessageOutput(QtMsgType type, const QMessageLogContext &context
void MainWindow::readSettings()
{
QSettings settings("D8S", "OpenStoryTeller");
restoreGeometry(settings.value("MainWindow/geometry").toByteArray());
restoreState(settings.value("MainWindow/windowState").toByteArray());
m_settings.setDefaultFormat(QSettings::IniFormat);
m_settings.setPath(QSettings::IniFormat, QSettings::UserScope, QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
restoreGeometry(m_settings.value("MainWindow/geometry").toByteArray());
restoreState(m_settings.value("MainWindow/windowState").toByteArray());
}
void MainWindow::closeEvent(QCloseEvent *event)
{
QSettings settings("D8S", "OpenStoryTeller");
settings.setValue("MainWindow/geometry", saveGeometry());
settings.setValue("MainWindow/windowState", saveState());
m_settings.setValue("MainWindow/geometry", saveGeometry());
m_settings.setValue("MainWindow/windowState", saveState());
QMainWindow::closeEvent(event);
}
@ -227,7 +273,7 @@ void MainWindow::DisplayNode(StoryNode *m_tree, QtNodes::NodeId parentId)
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;
nodeInternalData["image"] = imageFile.c_str();
}
@ -431,58 +477,99 @@ void MainWindow::createStatusBar()
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::open()
void MainWindow::CloseProject()
{
QMimeDatabase mimeDatabase;
QString fileName = QFileDialog::getOpenFileName(this,
tr("Choose a file name"), ".",
mimeDatabase.mimeTypeForName("application/json").filterString());
if (fileName.isEmpty())
return;
QFile file(fileName);
if (!file.open(QFile::ReadOnly | QFile::Text)) {
QMessageBox::warning(this, tr("Dock Widgets"),
tr("Cannot write file %1:\n%2.")
.arg(QDir::toNativeSeparators(fileName), file.errorString()));
m_project.Clear();
m_toolbar->SetAllDocks(false);
m_toolbar->SetActionsActive(false);
m_view->setEnabled(false);
}
void MainWindow::OpenProject()
{
QString fn = QFileDialog::getOpenFileName(this, tr("Open project file"),
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;
}
QTextStream in(&file);
QGuiApplication::setOverrideCursor(Qt::WaitCursor);
QJsonParseError err;
QJsonDocument loadDoc = QJsonDocument::fromJson(loadFile.readAll(), &err);
QJsonParseError error;
QString projectData = in.readAll();
if (err.error == QJsonParseError::NoError)
{
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()
{
// Open the dialog if the project file does not exists
if (!QFile::exists(m_project.file_path.c_str()))
{
// Save current project
QString fileName = QFileDialog::getSaveFileName(this, tr("Save project file"),
QDir::homePath() + "/new_story.ostproj",
tr("OpenStory Teller project (*.ostproj)"));
if (fileName.isEmpty())
return;
m_project.file_path = fileName.toStdString();
}
QJsonObject jsonModel = m_model.save();
QJsonObject projectData;
projectData["name"] = m_project.name.c_str();
// projectData["uuid"] = m_project.uuid.c_str();
QJsonObject saveData;
saveData["project"] = projectData;
saveData["nodegraph"] = jsonModel;
QJsonObject saveData = m_model.save();
QJsonDocument doc(saveData);
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()

View file

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

View file

@ -1,23 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>NewProjectDialog</class>
<widget class="QDialog" name="NewProjectDialog">
<class>newProjectDialog</class>
<widget class="QDialog" name="newProjectDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>346</width>
<height>263</height>
<height>269</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
<string>New project</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>New project parameters</string>
<string>New project parameters (directory must be empty)</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
@ -30,10 +30,14 @@
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit"/>
<widget class="QLineEdit" name="directoryPath">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<widget class="QPushButton" name="selectDirectoryButton">
<property name="minimumSize">
<size>
<width>40</width>
@ -63,7 +67,7 @@
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit_2"/>
<widget class="QLineEdit" name="projectName"/>
</item>
</layout>
</item>
@ -77,13 +81,7 @@
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBox">
<item>
<property name="text">
<string>320x240</string>
</property>
</item>
</widget>
<widget class="QComboBox" name="displaySizeCombo"/>
</item>
</layout>
</item>
@ -97,13 +95,7 @@
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBox_2">
<item>
<property name="text">
<string>BMP (compressed 4-bit palette)</string>
</property>
</item>
</widget>
<widget class="QComboBox" name="imageFormatCombo"/>
</item>
</layout>
</item>
@ -117,13 +109,7 @@
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBox_3">
<item>
<property name="text">
<string>QOA (Quite Ok Audio)</string>
</property>
</item>
</widget>
<widget class="QComboBox" name="soundFormatCombo"/>
</item>
</layout>
</item>
@ -140,6 +126,16 @@
</property>
</spacer>
</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>
</widget>
</item>
@ -156,38 +152,5 @@
</layout>
</widget>
<resources/>
<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>
<connections/>
</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/volume-off.png</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>
</RCC>

View file

@ -6,19 +6,31 @@
#include <filesystem>
#include "json.hpp"
void StoryProject::Initialize()
void StoryProject::New(const std::string &uuid, const std::string &file_path)
{
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
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_soundsPath = std::filesystem::path(working_dir) / "sounds";
m_imagesPath = std::filesystem::path(m_working_dir) / "images";
m_soundsPath = std::filesystem::path(m_working_dir) / "sounds";
std::filesystem::create_directories(m_imagesPath);
std::filesystem::create_directories(m_soundsPath);
m_initialized = true;
}
bool StoryProject::Load(const std::string &file_path)
@ -27,9 +39,9 @@ bool StoryProject::Load(const std::string &file_path)
bool success = false;
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 {
@ -89,7 +101,7 @@ bool StoryProject::Load(const std::string &file_path)
m_type = j["type"];
m_code = j["code"];
m_name = j["name"];
name = j["name"];
success = true;
@ -207,3 +219,29 @@ std::string StoryProject::Compile()
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
{
std::string uuid;
std::string working_dir; /// Temporary folder based on the uuid, where the archive is unzipped
std::string file_path; /// project file (archive)
enum ImageFormat { IMG_FORMAT_BMP_4BITS, IMG_FORMAT_QOIF, IMG_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::string m_type;
std::string m_code;
std::string m_name;
std::vector<Resource> m_images;
std::vector<Resource> m_sounds;
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);
void CreateTree();
void Clear() {
m_uuid = "";
m_working_dir = "";
m_images.clear();
m_sounds.clear();
m_initialized = false;
}
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 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);
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 std::string ToUpper(const std::string &input);
private:
std::string m_uuid;
std::filesystem::path m_imagesPath;
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

View file

@ -12,7 +12,7 @@ void ToolBar::createActions(QMenuBar *menuBar)
// ------------ 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);
act->setShortcuts(QKeySequence::Save);
act->setStatusTip(tr("Create a new project"));
@ -20,20 +20,19 @@ void ToolBar::createActions(QMenuBar *menuBar)
fileMenu->addAction(act);
addAction(act);
}
// ------------ Save
{
const QIcon icon = QIcon::fromTheme("document-save", QIcon(":/assets/floppy.svg"));
QAction *act = new QAction(icon, tr("&Save project"), this);
act->setShortcuts(QKeySequence::Save);
act->setStatusTip(tr("Save the current project"));
connect(act, &QAction::triggered, this, &ToolBar::sigSave);
fileMenu->addAction(act);
addAction(act);
QIcon icon(":/assets/floppy.svg");
m_saveProjectAction = new QAction(icon, tr("&Save project"), this);
m_saveProjectAction->setShortcuts(QKeySequence::Save);
m_saveProjectAction->setStatusTip(tr("Save the current project"));
connect(m_saveProjectAction, &QAction::triggered, this, &ToolBar::sigSave);
fileMenu->addAction(m_saveProjectAction);
addAction(m_saveProjectAction);
}
// ------------ 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);
act->setShortcuts(QKeySequence::Open);
act->setStatusTip(tr("Open an existing project"));
@ -41,10 +40,20 @@ void ToolBar::createActions(QMenuBar *menuBar)
fileMenu->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();
QAction *quitAct = fileMenu->addAction(tr("&Quit"), this, &QWidget::close);
QAction *quitAct = fileMenu->addAction(tr("&Quit"), this, &ToolBar::sigExit);
quitAct->setShortcuts(QKeySequence::Quit);
quitAct->setStatusTip(tr("Quit the application"));
@ -52,13 +61,51 @@ void ToolBar::createActions(QMenuBar *menuBar)
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"));
QAction *aboutAct = helpMenu->addAction(tr("&About"), this, &ToolBar::sigAbout);
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)
{
m_windowsMenu->addAction(action);
m_actionDockList.push_back(action);
}

View file

@ -11,17 +11,26 @@ class ToolBar : public QToolBar
public:
ToolBar();
void createActions(QMenuBar *menuBar);
void AddDockToMenu(QAction *action);
void SetAllDocks(bool enable);
void SetActionsActive(bool enable);
void ShowAllDocks(bool enable);
signals:
void sigNew();
void sigSave();
void sigOpen();
void sigClose();
void sigExit();
void sigAbout();
void sigDefaultDocksPosition();
private:
QMenu *m_windowsMenu;
QAction *m_closeAllDocksAction;
QAction *m_saveProjectAction;
QAction *m_closeProjectAction;
QList<QAction *> m_actionDockList;
};
#endif // TOOLBAR_H