diff --git a/docs/guide-devkit-pico.md b/docs/guide-devkit-pico.md index aaaeea6..6d8ecf7 100644 --- a/docs/guide-devkit-pico.md +++ b/docs/guide-devkit-pico.md @@ -13,18 +13,20 @@ 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 | -| **TOTAL** | **67 €** | +| 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... diff --git a/story-editor/CMakeLists.txt b/story-editor/CMakeLists.txt index e38478e..82532fa 100644 --- a/story-editor/CMakeLists.txt +++ b/story-editor/CMakeLists.txt @@ -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 diff --git a/story-editor/assets/close-outline.svg b/story-editor/assets/close-outline.svg new file mode 100644 index 0000000..6673e6f --- /dev/null +++ b/story-editor/assets/close-outline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/story-editor/assets/folder-open-outline.svg b/story-editor/assets/folder-open-outline.svg new file mode 100644 index 0000000..28af2ad --- /dev/null +++ b/story-editor/assets/folder-open-outline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/story-editor/assets/welcome.png b/story-editor/assets/welcome.png new file mode 100644 index 0000000..efc8524 Binary files /dev/null and b/story-editor/assets/welcome.png differ diff --git a/story-editor/src/main_window.cpp b/story-editor/src/main_window.cpp index f2a31cb..7227ad2 100644 --- a/story-editor/src/main_window.cpp +++ b/story-editor/src/main_window.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -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::func = std::bind(&MainWindow::MessageOutput, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); - auto cb = static_cast(Callback::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("MediaNode"); m_model.addModel("MediaNode", "Story Teller"); @@ -166,39 +169,81 @@ MainWindow::MainWindow() readSettings(); - connect(m_toolbar, &ToolBar::sigNew, this, [=]() { + connect(m_toolbar, &ToolBar::sigNew, this, [&]() { NewProject(); }); 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::func = std::bind(&MainWindow::MessageOutput, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); + auto cb = static_cast(Callback::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() diff --git a/story-editor/src/main_window.h b/story-editor/src/main_window.h index 4e10fdc..f7d4869 100644 --- a/story-editor/src/main_window.h +++ b/story-editor/src/main_window.h @@ -11,9 +11,9 @@ #include #include - #include - +#include +#include #include #include #include @@ -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 diff --git a/story-editor/src/new-project.ui b/story-editor/src/new-project.ui index 482bb8e..aed4d98 100644 --- a/story-editor/src/new-project.ui +++ b/story-editor/src/new-project.ui @@ -1,23 +1,23 @@ - NewProjectDialog - + newProjectDialog + 0 0 346 - 263 + 269 - Dialog + New project - New project parameters + New project parameters (directory must be empty) @@ -30,10 +30,14 @@ - + + + true + + - + 40 @@ -63,7 +67,7 @@ - + @@ -77,13 +81,7 @@ - - - - 320x240 - - - + @@ -97,13 +95,7 @@ - - - - BMP (compressed 4-bit palette) - - - + @@ -117,13 +109,7 @@ - - - - QOA (Quite Ok Audio) - - - + @@ -140,6 +126,16 @@ + + + + ErrorMessage + + + Qt::RichText + + + @@ -156,38 +152,5 @@ - - - buttonBox - accepted() - NewProjectDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - NewProjectDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - + diff --git a/story-editor/src/new_project_dialog.cpp b/story-editor/src/new_project_dialog.cpp new file mode 100644 index 0000000..c676514 --- /dev/null +++ b/story-editor/src/new_project_dialog.cpp @@ -0,0 +1,106 @@ +#include "new_project_dialog.h" +#include +#include + +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"(

)" + tr("Error, project name empty.") + R"(

)"); + } + } + else + { + m_ui.errorMessage->setText(R"(

)" + tr("Error, directory is not empty.") + R"(

)"); + } + } + else + { + m_ui.errorMessage->setText(R"(

)" + tr("Error, directory does not exist.") + R"(

)"); + } + + + }); + + 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(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(idx); + } + + return img; +} + +QSize NewProjectDialog::GetDisplayFormat() const +{ + return m_ui.displaySizeCombo->currentData().toSize(); +} + +void NewProjectDialog::Initialize() +{ + m_ui.errorMessage->clear(); + m_ui.directoryPath->clear(); +} diff --git a/story-editor/src/new_project_dialog.h b/story-editor/src/new_project_dialog.h new file mode 100644 index 0000000..e853f12 --- /dev/null +++ b/story-editor/src/new_project_dialog.h @@ -0,0 +1,32 @@ +#ifndef NEW_PROJECT_DIALOG_H +#define NEW_PROJECT_DIALOG_H + +#include +#include +#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 diff --git a/story-editor/src/nodeeditor_dock.cpp b/story-editor/src/nodeeditor_dock.cpp deleted file mode 100644 index 0d19d0a..0000000 --- a/story-editor/src/nodeeditor_dock.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "nodeeditor_dock.h" -#include - -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); -} diff --git a/story-editor/src/nodeeditor_dock.h b/story-editor/src/nodeeditor_dock.h deleted file mode 100644 index 53e0a53..0000000 --- a/story-editor/src/nodeeditor_dock.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef NODEEDITORDOCK_H -#define NODEEDITORDOCK_H - -#include "dock_widget_base.h" -#include -#include -using QtNodes::BasicGraphicsScene; -using QtNodes::GraphicsView; - - -class NodeEditorDock : public DockWidgetBase -{ -public: - NodeEditorDock(BasicGraphicsScene *scene); - -private: - GraphicsView *m_view{nullptr}; -}; - -#endif // NODEEDITORDOCK_H diff --git a/story-editor/src/ost-editor.qrc b/story-editor/src/ost-editor.qrc index 78d4e05..add32b2 100644 --- a/story-editor/src/ost-editor.qrc +++ b/story-editor/src/ost-editor.qrc @@ -6,5 +6,8 @@ ../assets/build.png ../assets/volume-off.png ../assets/file-document-plus-outline.svg + ../assets/close-outline.svg + ../assets/folder-open-outline.svg + ../assets/welcome.png diff --git a/story-editor/src/story_project.cpp b/story-editor/src/story_project.cpp index 37de09c..f8a41d2 100644 --- a/story-editor/src/story_project.cpp +++ b/story-editor/src/story_project.cpp @@ -6,19 +6,31 @@ #include #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; +} + diff --git a/story-editor/src/story_project.h b/story-editor/src/story_project.h index 610c61f..13e38ef 100644 --- a/story-editor/src/story_project.h +++ b/story-editor/src/story_project.h @@ -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 m_nodes; std::string m_type; std::string m_code; - std::string m_name; std::vector m_images; std::vector 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 diff --git a/story-editor/src/toolbar.cpp b/story-editor/src/toolbar.cpp index 101a781..3345bb7 100644 --- a/story-editor/src/toolbar.cpp +++ b/story-editor/src/toolbar.cpp @@ -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); } diff --git a/story-editor/src/toolbar.h b/story-editor/src/toolbar.h index 6fb12a2..2c4afb4 100644 --- a/story-editor/src/toolbar.h +++ b/story-editor/src/toolbar.h @@ -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 m_actionDockList; }; #endif // TOOLBAR_H