diff --git a/docs/guide-mkr-zero.md b/docs/guide-mkr-zero.md
index 768da0e..1aaa66d 100644
--- a/docs/guide-mkr-zero.md
+++ b/docs/guide-mkr-zero.md
@@ -1,5 +1,7 @@
## Arduino MKR Zero (SAMD21G18A)
+Current status: ON DEVELOPMENT
+
| Category | Maker | Name | Rounded Price |
| ------------------ | ------------------------------ | --------------------- | ------------- |
| Main CPU board | Arduino | MKR Zero | 30€ |
diff --git a/story-editor/assets/play-circle-green.png b/story-editor/assets/play-circle-green.png
new file mode 100644
index 0000000..8c7d84b
Binary files /dev/null and b/story-editor/assets/play-circle-green.png differ
diff --git a/story-editor/assets/play-circle-green.svg b/story-editor/assets/play-circle-green.svg
new file mode 100644
index 0000000..ac6e09a
--- /dev/null
+++ b/story-editor/assets/play-circle-green.svg
@@ -0,0 +1,41 @@
+
+
diff --git a/story-editor/src/main_window.cpp b/story-editor/src/main_window.cpp
index f3aa7b0..6a3a43d 100644
--- a/story-editor/src/main_window.cpp
+++ b/story-editor/src/main_window.cpp
@@ -135,10 +135,13 @@ MainWindow::MainWindow()
{
// Take first
QModelIndex index = selection.at(0);
- QString fn = m_resourcesDock->getModel().GetFileName(index.row());
- QJsonObject obj;
- obj["image"] = fn;
- m_model.setNodeData(id, NodeRole::InternalData, obj.toVariantMap());
+ Resource res;
+ if (m_project.GetResourceAt(index.row(), res))
+ {
+ QJsonObject obj;
+ obj["image"] = res.file.c_str();
+ m_model.setNodeData(id, NodeRole::InternalData, obj.toVariantMap());
+ }
}
});
@@ -268,7 +271,7 @@ void MainWindow::DisplayNode(StoryNode *m_tree, QtNodes::NodeId parentId)
if (m_tree->image >= 0)
{
- std::string imageFile = m_project.GetWorkingDir() + "/rf/" + m_project.m_images[m_tree->image].file + ".bmp";
+ std::string imageFile = ""; // FIXME 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();
}
@@ -575,6 +578,20 @@ void MainWindow::OpenProject(const QString &filePath)
m_project.SetName(projectData["name"].toString().toStdString());
m_project.SetUuid(projectData["uuid"].toString().toStdString());
+ QJsonArray resourcesData = projectData["resources"].toArray();
+
+ for (const auto &r : resourcesData)
+ {
+ Resource rData;
+ QJsonObject obj = r.toObject();
+ rData.type = obj["type"].toString().toStdString();
+ rData.format = obj["format"].toString().toStdString();
+ rData.description = obj["description"].toString().toStdString();
+ rData.file = obj["file"].toString().toStdString();
+ m_resourcesDock->Append(rData);
+ }
+
+
if (projectRoot.contains("nodegraph"))
{
QJsonObject nodeData = projectRoot["nodegraph"].toObject();
@@ -622,6 +639,21 @@ void MainWindow::SaveProject()
projectData["name"] = m_project.GetName().c_str();
projectData["uuid"] = m_project.GetUuid().c_str();
+ QJsonArray resourcesData;
+
+ for (std::vector::const_iterator it = m_project.Begin(); it != m_project.End(); ++it)
+ {
+ auto &r = *it;
+ QJsonObject obj;
+ obj["type"] = r.type.c_str();
+ obj["format"] = r.format.c_str();
+ obj["description"] = r.description.c_str();
+ obj["file"] = r.file.c_str();
+
+ resourcesData.append(obj);
+ }
+ projectData["resources"] = resourcesData;
+
QJsonObject saveData;
saveData["project"] = projectData;
saveData["nodegraph"] = jsonModel;
diff --git a/story-editor/src/media_node_model.cpp b/story-editor/src/media_node_model.cpp
index cbd320b..8a09c88 100644
--- a/story-editor/src/media_node_model.cpp
+++ b/story-editor/src/media_node_model.cpp
@@ -42,7 +42,6 @@ MediaNodeModel::MediaNodeModel(StoryGraphModel &model)
});
connect(m_ui.selectImageButton, &QPushButton::clicked, [&](bool enable) {
- //m_contextMenu->exec(m_widget->mapFromGlobal(QCursor::pos()));
emit m_model.sigChooseFile(getNodeId());
});
}
diff --git a/story-editor/src/ost-editor.qrc b/story-editor/src/ost-editor.qrc
index add32b2..67a9b1b 100644
--- a/story-editor/src/ost-editor.qrc
+++ b/story-editor/src/ost-editor.qrc
@@ -9,5 +9,6 @@
../assets/close-outline.svg
../assets/folder-open-outline.svg
../assets/welcome.png
+ ../assets/play-circle-green.png
diff --git a/story-editor/src/resource_model.cpp b/story-editor/src/resource_model.cpp
index 9b7cee5..a3501b0 100644
--- a/story-editor/src/resource_model.cpp
+++ b/story-editor/src/resource_model.cpp
@@ -1,15 +1,37 @@
#include "resource_model.h"
+ResourceModel::ResourceModel(StoryProject &project, QObject *parent)
+ : QAbstractTableModel{parent}
+ , m_project(project)
+{
+
+}
+
+int ResourceModel::rowCount(const QModelIndex &) const
+{
+ return m_project.ResourcesSize();
+}
+
+int ResourceModel::columnCount(const QModelIndex &) const
+{
+ return 4;
+}
+
QVariant ResourceModel::data(const QModelIndex &index, int role) const {
if (role != Qt::DisplayRole && role != Qt::EditRole) return {};
- const auto & res = m_data[index.row()];
- switch (index.column()) {
- case 0: return res.file.c_str();
- case 1: return res.format.c_str();
- case 2: return res.description.c_str();
- default: return {};
- };
+ Resource res;
+ if (m_project.GetResourceAt(index.row(), res))
+ {
+ switch (index.column()) {
+ case 0: return res.file.c_str();
+ case 1: return res.format.c_str();
+ case 2: return res.description.c_str();
+ case 3: return res.type.c_str();
+ default: ;
+ };
+ }
+ return {};
}
QVariant ResourceModel::headerData(int section, Qt::Orientation orientation, int role) const {
@@ -18,30 +40,76 @@ QVariant ResourceModel::headerData(int section, Qt::Orientation orientation, int
case 0: return "File";
case 1: return "Format";
case 2: return "Description";
+ case 3: return "Type";
default: return {};
}
}
void ResourceModel::append(const Resource &res) {
- beginInsertRows({}, m_data.count(), m_data.count());
- m_data.append(res);
+ beginInsertRows({}, m_project.ResourcesSize(), m_project.ResourcesSize());
+ m_project.AppendResource(res);
endInsertRows();
}
void ResourceModel::Clear()
{
beginResetModel();
- m_data.clear();
+ m_project.ClearResources();
endResetModel();
}
-QString ResourceModel::GetFileName(int row) {
- QString n;
- if (row < m_data.size())
- {
- n = m_data.at(row).file.c_str();
- }
+// ------------------------------- PROXY MODEL -------------------------------
- return n;
+
+ResourceFilterProxyModel::ResourceFilterProxyModel(QObject *parent)
+ : QSortFilterProxyModel(parent)
+{
}
+
+void ResourceFilterProxyModel::setFilterType(const QString & type)
+{
+ m_typeFilter = type;
+ invalidateFilter();
+}
+
+
+bool ResourceFilterProxyModel::filterAcceptsRow(int sourceRow,
+ const QModelIndex &sourceParent) const
+{
+ QModelIndex indexType = sourceModel()->index(sourceRow, 3, sourceParent);
+
+ return (sourceModel()->data(indexType).toString() == m_typeFilter);
+}
+/*
+bool ResourceFilterProxyModel::lessThan(const QModelIndex &left,
+ const QModelIndex &right) const
+{
+ QVariant leftData = sourceModel()->data(left);
+ QVariant rightData = sourceModel()->data(right);
+
+ if (leftData.userType() == QMetaType::QDateTime) {
+ return leftData.toDateTime() < rightData.toDateTime();
+ } else {
+ static const QRegularExpression emailPattern("[\\w\\.]*@[\\w\\.]*");
+
+ QString leftString = leftData.toString();
+ if (left.column() == 1) {
+ const QRegularExpressionMatch match = emailPattern.match(leftString);
+ if (match.hasMatch())
+ leftString = match.captured(0);
+ }
+ QString rightString = rightData.toString();
+ if (right.column() == 1) {
+ const QRegularExpressionMatch match = emailPattern.match(rightString);
+ if (match.hasMatch())
+ rightString = match.captured(0);
+ }
+
+ return QString::localeAwareCompare(leftString, rightString) < 0;
+ }
+}
+
+*/
+
+
diff --git a/story-editor/src/resource_model.h b/story-editor/src/resource_model.h
index 62d17a6..03ee802 100644
--- a/story-editor/src/resource_model.h
+++ b/story-editor/src/resource_model.h
@@ -10,22 +10,36 @@ class ResourceModel : public QAbstractTableModel
{
public:
- ResourceModel(QObject * parent = {}) : QAbstractTableModel{parent} {}
+ ResourceModel(StoryProject &project, QObject * parent = {});
- int rowCount(const QModelIndex &) const override { return m_data.count(); }
- int columnCount(const QModelIndex &) const override { return 3; }
+ int rowCount(const QModelIndex &) const override;
+ int columnCount(const QModelIndex &) const override;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
void append(const Resource & res);
void Clear();
- QString GetFileName(int row);
+private:
+ StoryProject &m_project;
+};
- QList GetData() const { return m_data; }
+class ResourceFilterProxyModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+
+public:
+ ResourceFilterProxyModel(QObject *parent = nullptr);
+
+
+ void setFilterType(const QString &type);
+protected:
+ bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
+// bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
private:
- QList m_data;
+
+ QString m_typeFilter;
};
#endif // RESOURCE_MODEL_H
diff --git a/story-editor/src/resources_dock.cpp b/story-editor/src/resources_dock.cpp
index 149c04d..7f1c155 100644
--- a/story-editor/src/resources_dock.cpp
+++ b/story-editor/src/resources_dock.cpp
@@ -4,6 +4,7 @@
ResourcesDock::ResourcesDock(StoryProject &project)
: m_project(project)
+ , m_resourcesModel(project)
, DockWidgetBase(tr("Resources"), true)
{
setObjectName("ResourcesDock"); // used to save the state
@@ -11,37 +12,46 @@ ResourcesDock::ResourcesDock(StoryProject &project)
m_uiOstResources.setupUi(this);
m_uiOstResources.resourcesView->setModel(&m_resourcesModel);
+ m_proxyModel.setSourceModel(&m_resourcesModel);
+
connect(m_uiOstResources.addImageButton, &QPushButton::clicked, [=](bool checked) {
QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"),
".",
tr("Images (*.bmp)"));
- std::filesystem::path p(fileName.toStdString());
- std::filesystem::path p2 = m_project.ImagesPath() / p.filename().generic_string();
- std::filesystem::copy(p, p2);
-
- Resource res;
- res.format = "BMP";
- res.file = p.filename();
- m_resourcesModel.append(res);
+ if (std::filesystem::exists(fileName.toStdString()))
+ {
+ std::filesystem::path p(fileName.toStdString());
+ std::filesystem::path p2 = m_project.ImagesPath() / p.filename().generic_string();
+ std::filesystem::copy(p, p2, std::filesystem::copy_options::overwrite_existing);
+ Resource res;
+ res.format = "BMP";
+ res.type = "image";
+ res.file = p.filename();
+ m_resourcesModel.append(res);
+ }
});
connect(m_uiOstResources.addSoundButton, &QPushButton::clicked, [=](bool checked) {
QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"),
".",
- tr("Sounds (*.wav)"));
+ tr("Sound files (*.wav, *.mp3, *.m4a)"));
- std::filesystem::path p(fileName.toStdString());
- std::filesystem::path p2 = m_project.SoundsPath() / p.filename().generic_string();
- std::filesystem::copy(p, p2);
+ if (std::filesystem::exists(fileName.toStdString()))
+ {
+ std::filesystem::path p(fileName.toStdString());
+ std::filesystem::path p2 = m_project.SoundsPath() / p.filename().generic_string();
+ std::filesystem::copy(p, p2, std::filesystem::copy_options::overwrite_existing);
- Resource res;
- res.format = "WAV";
- res.file = p.filename();
- m_resourcesModel.append(res);
+ Resource res;
+ res.format = "WAV";
+ res.type = "sound";
+ res.file = p.filename();
+ m_resourcesModel.append(res);
+ }
});
}
@@ -50,16 +60,21 @@ void ResourcesDock::Initialize()
}
+void ResourcesDock::Append(const Resource &res)
+{
+ m_resourcesModel.append(res);
+}
+
void ResourcesDock::SaveToProject()
{
m_project.Clear();
- for (auto & r : m_resourcesModel.GetData())
- {
- m_project.m_images.push_back(r);
- }
+// for (auto & r : m_resourcesModel.GetData())
+// {
+// m_project.m_images.push_back(r);
+ // }
}
-void ResourcesDock::slotClear()
+void ResourcesDock::Clear()
{
m_resourcesModel.Clear();
}
diff --git a/story-editor/src/resources_dock.h b/story-editor/src/resources_dock.h
index 08f7712..52cd2f8 100644
--- a/story-editor/src/resources_dock.h
+++ b/story-editor/src/resources_dock.h
@@ -14,18 +14,21 @@ public:
void Initialize();
- QList GetResources() const { return m_resourcesModel.GetData(); }
ResourceModel &getModel() { return m_resourcesModel; }
+ ResourceFilterProxyModel &getFilteredModel() { return m_proxyModel; }
+
+ void SetFilterType(const QString &type) { m_proxyModel.setFilterType(type); }
+
+ void Append(const Resource &res);
void SaveToProject();
-
-public slots:
- void slotClear();
+ void Clear();
private:
StoryProject &m_project;
Ui::ostResources m_uiOstResources;
ResourceModel m_resourcesModel;
+ ResourceFilterProxyModel m_proxyModel;
};
#endif // RESOURCESDOCK_H
diff --git a/story-editor/src/story_project.cpp b/story-editor/src/story_project.cpp
index 0301bf9..f8f800f 100644
--- a/story-editor/src/story_project.cpp
+++ b/story-editor/src/story_project.cpp
@@ -35,9 +35,10 @@ void StoryProject::Initialize(const std::string &file_path)
bool StoryProject::Load(const std::string &file_path)
{
+
std::ifstream f(file_path);
bool success = false;
-
+/*
std::filesystem::path p(file_path);
m_working_dir= p.parent_path();
@@ -116,7 +117,7 @@ bool StoryProject::Load(const std::string &file_path)
{
CreateTree();
}
-
+*/
return success;
}
@@ -191,6 +192,27 @@ void StoryProject::ReplaceCharacter(std::string &theString, const std::string &t
while (found != std::string::npos);
}
+void StoryProject::AppendResource(const Resource &res)
+{
+ m_resources.push_back(res);
+}
+
+bool StoryProject::GetResourceAt(int index, Resource &resOut)
+{
+ bool success = false;
+ if ((index >= 0) && (index < m_resources.size()))
+ {
+ resOut = m_resources[index];
+ success = true;
+ }
+ return success;
+}
+
+void StoryProject::ClearResources()
+{
+ m_resources.clear();
+}
+
std::string StoryProject::GetFileExtension(const std::string &fileName)
{
if(fileName.find_last_of(".") != std::string::npos)
@@ -204,7 +226,7 @@ std::string StoryProject::Compile()
chip32 << "\tjump .entry\r\n";
- for (auto & r : m_images)
+ for (auto & r : m_resources)
{
std::string fileName = GetFileName(r.file);
std::string ext = GetFileExtension(fileName);
diff --git a/story-editor/src/story_project.h b/story-editor/src/story_project.h
index f30e5fe..57ca8e4 100644
--- a/story-editor/src/story_project.h
+++ b/story-editor/src/story_project.h
@@ -40,6 +40,7 @@ struct Resource
std::string file;
std::string description;
std::string format;
+ std::string type;
};
struct StoryProject
@@ -52,20 +53,14 @@ struct StoryProject
std::string m_type;
std::string m_code;
- std::vector m_images;
- std::vector m_sounds;
-
StoryNode *m_tree;
-
bool Load(const std::string &file_path);
void CreateTree();
void Clear() {
m_uuid = "";
m_working_dir = "";
- m_images.clear();
- m_sounds.clear();
-
+ m_resources.clear();
m_initialized = false;
}
@@ -88,6 +83,17 @@ struct StoryProject
static std::string GetFileName(const std::string &path);
static void ReplaceCharacter(std::string &theString, const std::string &toFind, const std::string &toReplace);
+
+ // ------------- Resources Management
+ void AppendResource(const Resource &res);
+ bool GetResourceAt(int index, Resource &resOut);
+ void ClearResources();
+ int ResourcesSize() const { return m_resources.size(); }
+
+ std::vector::const_iterator Begin() const { return m_resources.begin(); }
+ std::vector::const_iterator End() const { return m_resources.end(); }
+
+
public:
// Initialize with an existing project
void Initialize(const std::string &file_path);
@@ -104,6 +110,7 @@ private:
std::filesystem::path m_soundsPath;
bool m_initialized{false};
+ std::vector m_resources;
std::string m_working_dir; /// Temporary folder based on the uuid, where the archive is unzipped
std::string m_project_path; /// JSON project file
diff --git a/story-editor/src/toolbar.cpp b/story-editor/src/toolbar.cpp
index a5590c9..09a25d0 100644
--- a/story-editor/src/toolbar.cpp
+++ b/story-editor/src/toolbar.cpp
@@ -67,6 +67,21 @@ void ToolBar::createActions(QMenuBar *menuBar)
menuBar->addSeparator();
+ // -------------------------------- DEBUG STORY
+
+ QMenu *storyMenu = menuBar->addMenu(tr("&Story"));
+ // ------------ Run
+ {
+ QIcon icon(":/assets/play-circle-green.png");
+ m_runAction = new QAction(icon, tr("&Close project"), this);
+ m_runAction->setStatusTip(tr("Build and run current project"));
+ connect(m_runAction, &QAction::triggered, this, &ToolBar::sigRun);
+ storyMenu->addAction(m_runAction);
+ addSeparator();
+ addAction(m_runAction);
+ }
+
+ // -------------------------------- WINDOWS MENU
m_windowsMenu = menuBar->addMenu(tr("&Windows"));
auto act = m_windowsMenu->addAction(tr("Reset docks position"));
@@ -102,6 +117,7 @@ void ToolBar::SetActionsActive(bool enable)
m_windowsMenu->setEnabled(enable);
m_closeProjectAction->setEnabled(enable);
m_saveProjectAction->setEnabled(enable);
+ m_runAction->setEnabled(enable);
}
diff --git a/story-editor/src/toolbar.h b/story-editor/src/toolbar.h
index d312a91..c480bbd 100644
--- a/story-editor/src/toolbar.h
+++ b/story-editor/src/toolbar.h
@@ -23,15 +23,17 @@ signals:
void sigExit();
void sigDefaultDocksPosition();
void sigOpenRecent(const QString &project);
+ void sigRun();
private slots:
void slotAbout();
private:
- QMenu *m_windowsMenu;
- QMenu *m_recentProjectsMenu;
- QAction *m_saveProjectAction;
- QAction *m_closeProjectAction;
+ QMenu *m_windowsMenu{nullptr};
+ QMenu *m_recentProjectsMenu{nullptr};
+ QAction *m_saveProjectAction{nullptr};
+ QAction *m_closeProjectAction{nullptr};
+ QAction *m_runAction{nullptr};
QList m_actionDockList;
};