save docks visibility, fix node, media node complete, vpfr generation (WIP)

This commit is contained in:
Anthony Rabine 2023-05-17 15:51:00 +02:00
parent 35b990f21f
commit 416b6b2f43
24 changed files with 471 additions and 80 deletions

4
.gitignore vendored
View file

@ -16,3 +16,7 @@ story-editor/CMakeLists.txt.user
hardware/kicad/ost-pico-addon/ost-pico-addon-backups/
hardware/kicad/ost-pico-addon/fp-info-cache
*.kra~
*.png~

122
art/pack/LICENSE.md Normal file
View file

@ -0,0 +1,122 @@
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.

2
art/pack/README.md Normal file
View file

@ -0,0 +1,2 @@
All arts in this directory are licensed under the Creative Commons CS0 PUBLIC DOMAIN.

BIN
art/pack/bird.kra Normal file

Binary file not shown.

BIN
art/pack/bird.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

View file

@ -9,11 +9,11 @@ set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets OpenGLWidgets OpenGL)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Widgets Gui OpenGL OpenGLWidgets)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets OpenGLWidgets Multimedia OpenGL)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Widgets Gui OpenGLWidgets Multimedia OpenGL )
set(PROJECT_SOURCES
src/main.cpp
@ -113,6 +113,7 @@ target_link_libraries(${PROJECT_NAME} PUBLIC
Qt${QT_VERSION_MAJOR}::Gui
Qt${QT_VERSION_MAJOR}::OpenGLWidgets
Qt${QT_VERSION_MAJOR}::OpenGL
Qt${QT_VERSION_MAJOR}::Multimedia
QtNodes
QHexView
)

@ -1 +1 @@
Subproject commit 91a32d82b38e30459131307f3c23b88a5d4de331
Subproject commit 3b9ae0777cd52d6037bdcc6e0b05b57c5eb8701e

View file

@ -43,4 +43,9 @@ void DockWidgetBase::Open()
}
}
void DockWidgetBase::SetPreferedVisibility(bool visibility)
{
m_visibility = visibility ? tribool::True : tribool::False;
}

View file

@ -21,7 +21,8 @@ public:
void Close();
void Open();
void SetPreferedVisibility(bool visibility);
bool GetPreferedVisibility() const { return m_visibility; }
private:
enum tribool: uint8_t {False = 0, True = 1, Unknown = 2};
tribool m_visibility{Unknown};

View file

@ -76,11 +76,11 @@ MainWindow::MainWindow()
m_logDock = new LogDock();
addDockWidget(Qt::DockWidgetArea::BottomDockWidgetArea, m_logDock);
m_toolbar->AddDockToMenu(m_logDock->toggleViewAction());
m_toolbar->AddDockToMenu(m_logDock->toggleViewAction(), m_logDock);
m_ostHmiDock = new OstHmiDock();
addDockWidget(Qt::DockWidgetArea::RightDockWidgetArea, m_ostHmiDock);
m_toolbar->AddDockToMenu(m_ostHmiDock->toggleViewAction());
m_toolbar->AddDockToMenu(m_ostHmiDock->toggleViewAction(), m_ostHmiDock);
connect(m_ostHmiDock, &OstHmiDock::sigOkButton, [=]() {
QCoreApplication::postEvent(this, new VmEvent(VmEvent::evOkButton));
@ -88,15 +88,15 @@ MainWindow::MainWindow()
m_resourcesDock = new ResourcesDock(m_project, m_resourceModel);
addDockWidget(Qt::DockWidgetArea::RightDockWidgetArea, m_resourcesDock);
m_toolbar->AddDockToMenu(m_resourcesDock->toggleViewAction());
m_toolbar->AddDockToMenu(m_resourcesDock->toggleViewAction(), m_resourcesDock);
m_scriptEditorDock = new ScriptEditorDock();
addDockWidget(Qt::DockWidgetArea::LeftDockWidgetArea, m_scriptEditorDock);
m_toolbar->AddDockToMenu(m_scriptEditorDock->toggleViewAction());
m_toolbar->AddDockToMenu(m_scriptEditorDock->toggleViewAction(), m_scriptEditorDock);
m_vmDock = new VmDock(m_assembler);
addDockWidget(Qt::DockWidgetArea::RightDockWidgetArea, m_vmDock);
m_toolbar->AddDockToMenu(m_vmDock->toggleViewAction());
m_toolbar->AddDockToMenu(m_vmDock->toggleViewAction(), m_vmDock);
connect(m_vmDock, &VmDock::sigCompile, [=]() {
// m_scriptEditorDock->setScript(m_project.BuildResources());
@ -112,11 +112,11 @@ MainWindow::MainWindow()
m_ramView = new MemoryViewDock("RamViewDock", "RAM");
addDockWidget(Qt::DockWidgetArea::RightDockWidgetArea, m_ramView);
m_toolbar->AddDockToMenu(m_ramView->toggleViewAction());
m_toolbar->AddDockToMenu(m_ramView->toggleViewAction(), m_ramView);
m_romView = new MemoryViewDock("RomViewDock", "ROM");
addDockWidget(Qt::DockWidgetArea::RightDockWidgetArea, m_romView);
m_toolbar->AddDockToMenu(m_romView->toggleViewAction());
m_toolbar->AddDockToMenu(m_romView->toggleViewAction(), m_romView);
tabifyDockWidget(m_vmDock, m_romView);
tabifyDockWidget(m_romView, m_ramView);
@ -208,7 +208,7 @@ MainWindow::MainWindow()
qInstallMessageHandler(cb);
readSettings();
readSettings(); // restore all windows preferences
qDebug() << "Settings location: " << m_settings.fileName();
qDebug() << "Welcome to StoryTeller Editor";
@ -221,7 +221,6 @@ MainWindow::MainWindow()
void MainWindow::BuildAndRun()
{
// 1. Check if the model can be compiled, check for errors and report
// FIXME
// 2. Generate the assembly code from the model
@ -231,7 +230,7 @@ void MainWindow::BuildAndRun()
// Add global functions
code += ReadResourceFile(":/scripts/media.asm").toStdString();
code += "\thalt\r\n";
// code += "\thalt\r\n";
m_scriptEditorDock->setScript(code.c_str());
@ -298,6 +297,9 @@ void MainWindow::readSettings()
// Restore recent projects list
m_recentProjects = m_settings.value("RecentProjects").toStringList();
m_toolbar->GenerateRecentProjectsMenu(m_recentProjects);
// retore prefered visibility
m_toolbar->SetDocksPreferences(m_settings.value("PreferedVisibility"));
}
void MainWindow::closeEvent(QCloseEvent *event)
@ -308,6 +310,9 @@ void MainWindow::closeEvent(QCloseEvent *event)
// Memorize recent projects list
m_settings.setValue("RecentProjects", m_recentProjects);
// Memorize all docks prefered visibiliy
m_settings.setValue("PreferedVisibility", m_toolbar->GetDocksPreferences());
QMainWindow::closeEvent(event);
}

View file

@ -34,6 +34,9 @@
<property name="text">
<string>TextLabel</string>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item>
@ -59,9 +62,9 @@
</spacer>
</item>
<item>
<widget class="QLabel" name="label_4">
<widget class="QLabel" name="imageName">
<property name="text">
<string>TextLabel</string>
<string/>
</property>
</widget>
</item>
@ -97,9 +100,9 @@
</spacer>
</item>
<item>
<widget class="QLabel" name="label_5">
<widget class="QLabel" name="soundName">
<property name="text">
<string>TextLabel</string>
<string/>
</property>
</widget>
</item>
@ -111,7 +114,7 @@
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_4">
<widget class="QPushButton" name="playSoundButton">
<property name="icon">
<iconset resource="ost-editor.qrc">
<normaloff>:/assets/play-circle.png</normaloff>:/assets/play-circle.png</iconset>

View file

@ -8,7 +8,7 @@
#include <QtCore/QEvent>
#include <QtWidgets/QFileDialog>
#include <QMenu>
#include <qdebug.h>
MediaNodeModel::MediaNodeModel(StoryGraphModel &model)
: m_model(model)
@ -46,6 +46,16 @@ MediaNodeModel::MediaNodeModel(StoryGraphModel &model)
emit m_model.sigChooseFile(getNodeId(), "image");
});
connect(m_ui.selectSoundButton, &QPushButton::clicked, [&](bool enable) {
emit m_model.sigChooseFile(getNodeId(), "sound");
});
connect(m_ui.playSoundButton, &QPushButton::clicked, [&](bool enable) {
m_model.PlaySound(m_soundFilePath);
});
m_ui.playSoundButton->setEnabled(false);
// default model
m_mediaData = {
{"image", ""},
@ -55,6 +65,11 @@ MediaNodeModel::MediaNodeModel(StoryGraphModel &model)
m_mediaData.merge_patch(StoryNodeBase::ToJson());
}
QString MediaNodeModel::caption() const
{
return QString("Media Node " + QString::number(getNodeId()));
}
nlohmann::json MediaNodeModel::ToJson() const
{
// Always start with generic
@ -66,30 +81,23 @@ nlohmann::json MediaNodeModel::ToJson() const
void MediaNodeModel::FromJson(nlohmann::json &j)
{
m_mediaData.merge_patch(j);
// Display loaded image
std::string imagePath = m_mediaData["image"].get<std::string>();
if (imagePath.size() > 0)
{
setImage(imagePath.c_str());
}
setInternalData(j); // Merge with SetInternalData ?
}
void MediaNodeModel::setImage(const QString &imagePath)
void MediaNodeModel::setImage(const QString &fileName)
{
QPixmap pix(imagePath);
QPixmap pix(m_model.BuildFullImagePath(fileName));
if (pix.isNull())
{
std::cout << "!!!!!!! " << m_mediaData["image"].get<std::string>() << std::endl;
qCritical() << "Can't find image: " << fileName;
}
int w = m_ui.image->width();
int h = m_ui.image->height();
pix.scaled(w, h, Qt::KeepAspectRatio);
m_ui.image->setPixmap(pix);
m_ui.imageName->setText(fileName);
}
void MediaNodeModel::setInternalData(const nlohmann::json &j)
@ -98,10 +106,74 @@ void MediaNodeModel::setInternalData(const nlohmann::json &j)
setImage(j["image"].get<std::string>().c_str());
}
if (j.contains("sound")) {
QString fileName = j["sound"].get<std::string>().c_str();
m_soundFilePath = m_model.BuildFullSoundPath(fileName);
m_ui.soundName->setText(fileName);
m_ui.playSoundButton->setEnabled(true);
}
// Merge new data into local object
m_mediaData.merge_patch(j);
}
std::string MediaNodeModel::GenerateConstants()
{
std::string s;
std::string image = m_mediaData["image"].get<std::string>();
std::string sound = m_mediaData["sound"].get<std::string>();
if (image.size() > 0)
{
s = StoryProject::FileToConstant(image);
}
if (sound.size() > 0)
{
s = StoryProject::FileToConstant(sound);
}
// Generate choice table if needed (out ports > 1)
return s;
}
std::string MediaNodeModel::Build()
{
std::stringstream ss;
ss << R"(; ---------------- )" << GetNodeTitle() << "\n";
std::string image = m_mediaData["image"].get<std::string>();
std::string sound = m_mediaData["sound"].get<std::string>();
if (image.size() > 0)
{
ss << "lcons r0, $" << image << "\n";
}
else
{
ss << "lcons r0, 0\n";
}
if (sound.size() > 0)
{
ss << "lcons r1, $" << sound << "\n";
}
else
{
ss << "lcons r1, 0\n";
}
/*
syscall 1
lcons r0, $ChoiceObject
jump .media ; no return possible, so a jump is enough
*/
return ss.str();
}
unsigned int MediaNodeModel::nPorts(PortType portType) const
{
unsigned int result = 1;

View file

@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2023-2099 Anthony Rabine <anthony@rabine.fr>
#pragma once
#include <iostream>
@ -18,7 +21,6 @@ using QtNodes::PortType;
#include "story_graph_model.h"
#include "story_node_base.h"
#include "story_project.h"
/// The model dictates the number of inputs and outputs for the Node.
/// In this example it has no logic.
@ -31,7 +33,7 @@ public:
~MediaNodeModel() = default;
public:
QString caption() const override { return QString("Media Node"); }
QString caption() const override;
QString name() const override { return QString("MediaNode"); }
@ -41,6 +43,9 @@ public:
void setInternalData(const nlohmann::json &j) override;
virtual std::string GenerateConstants() override;
virtual std::string Build() override;
public:
virtual QString modelName() const { return QString("MediaNode"); }
@ -66,6 +71,8 @@ private:
unsigned int m_ports{1};
QWidget *m_widget;
QString m_soundFilePath;
Ui::mediaNodeUi m_ui;
std::shared_ptr<NodeData> m_nodeData;

View file

@ -45,12 +45,19 @@ QVariant ResourceModel::headerData(int section, Qt::Orientation orientation, int
}
}
void ResourceModel::append(const Resource &res) {
void ResourceModel::Append(const Resource &res) {
beginInsertRows({}, m_project.ResourcesSize(), m_project.ResourcesSize());
m_project.AppendResource(res);
endInsertRows();
}
void ResourceModel::Delete(int row)
{
beginRemoveRows({}, row, row);
m_project.DeleteResourceAt(row);
endRemoveRows();
}
void ResourceModel::Clear()
{
beginResetModel();

View file

@ -16,8 +16,9 @@ public:
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 Append(const Resource & res);
void Delete(int row);
void Clear();
void BeginChange();
void EndChange();

View file

@ -18,7 +18,7 @@ ResourcesDock::ResourcesDock(StoryProject &project, ResourceModel &model)
QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"),
".",
tr("Images (*.bmp)"));
tr("Images (*.bmp *.png *.jpg)"));
if (std::filesystem::exists(fileName.toStdString()))
{
@ -30,7 +30,7 @@ ResourcesDock::ResourcesDock(StoryProject &project, ResourceModel &model)
res.format = "BMP";
res.type = "image";
res.file = p.filename();
m_resourcesModel.append(res);
m_resourcesModel.Append(res);
}
});
@ -38,7 +38,7 @@ ResourcesDock::ResourcesDock(StoryProject &project, ResourceModel &model)
QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"),
".",
tr("Sound files (*.wav, *.mp3, *.m4a)"));
tr("Sound files (*.wav *.mp3 *.m4a)"));
if (std::filesystem::exists(fileName.toStdString()))
{
@ -50,7 +50,21 @@ ResourcesDock::ResourcesDock(StoryProject &project, ResourceModel &model)
res.format = "WAV";
res.type = "sound";
res.file = p.filename();
m_resourcesModel.append(res);
m_resourcesModel.Append(res);
}
});
connect(m_uiOstResources.deleteButton, &QPushButton::clicked, [=](bool checked) {
QItemSelectionModel *selectionModel = m_uiOstResources.resourcesView->selectionModel();
// Récupération des lignes sélectionnées
QModelIndexList selectedRows = selectionModel->selectedRows();
if (selectedRows.count() > 0)
{
for (int i = 0; i < selectedRows.count(); i++)
{
m_resourcesModel.Delete(selectedRows.at(i).row());
}
}
});
}

View file

@ -4,11 +4,14 @@
#include <QtNodes/ConnectionIdUtils>
#include <QJsonArray>
#include <iterator>
#include <QDir>
StoryGraphModel::StoryGraphModel(StoryProject &project)
: m_project(project)
{
m_player = new QMediaPlayer;
m_audioOutput = new QAudioOutput;
m_player->setAudioOutput(m_audioOutput);
}
StoryGraphModel::~StoryGraphModel()
@ -146,7 +149,7 @@ QVariant StoryGraphModel::nodeData(NodeId nodeId, NodeRole role) const
break;
case NodeRole::Caption:
result = QString("Node");
result = model->caption();
break;
case NodeRole::Style: {
@ -168,7 +171,6 @@ QVariant StoryGraphModel::nodeData(NodeId nodeId, NodeRole role) const
case NodeRole::Widget: {
auto w = model->embeddedWidget();
result = QVariant::fromValue(w);
// result = QVariant::fromValue(widget(nodeId));
break;
}
}
@ -321,7 +323,7 @@ namespace QtNodes {
j = nlohmann::json{
{"outNodeId", static_cast<qint64>(p.outNodeId)},
{"outPortIndex", static_cast<qint64>(p.outPortIndex)},
{"intNodeId", static_cast<qint64>(p.inNodeId)},
{"inNodeId", static_cast<qint64>(p.inNodeId)},
{"inPortIndex", static_cast<qint64>(p.inPortIndex)},
};
}
@ -329,7 +331,7 @@ namespace QtNodes {
void from_json(const nlohmann::json& j, ConnectionId& p) {
j.at("outNodeId").get_to(p.outNodeId);
j.at("outPortIndex").get_to(p.outPortIndex);
j.at("intNodeId").get_to(p.inNodeId);
j.at("inNodeId").get_to(p.inNodeId);
j.at("inPortIndex").get_to(p.inPortIndex);
}
} // namespace QtNodes
@ -401,16 +403,11 @@ nlohmann::json StoryGraphModel::SaveNode(NodeId const nodeId) const
return nodeJson;
}
std::string StoryGraphModel::BuildNode(NodeId const nodeId) const
void StoryGraphModel::PlaySound(const QString &fileName)
{
std::string code;
auto it = _models.find(nodeId);
if (it == _models.end())
return "";
auto &model = it->second;
return model->Build();
m_player->setSource(QUrl::fromLocalFile(fileName));
m_audioOutput->setVolume(50);
m_player->play();
}
@ -460,16 +457,67 @@ void StoryGraphModel::LoadNode(const nlohmann::json &nodeJson)
}
}
std::string StoryGraphModel::Build()
NodeId StoryGraphModel::FindFirstNode() const
{
std::string code;
nlohmann::json nodesJsonArray;
for (auto const nodeId : allNodeIds()) {
NodeId id{InvalidNodeId};
code = BuildNode(nodeId) + "\n";
// First node is the one without connection on its input port
for (auto const nodeId : allNodeIds())
{
bool foundConnection{false};
for (auto& c : _connectivity)
{
if (c.outNodeId == nodeId)
{
foundConnection = true;
}
return code;
}
if (!foundConnection)
{
id = nodeId;
qDebug() << "First node is: " << id;
break;
}
}
return id;
}
std::string StoryGraphModel::Build()
{
std::stringstream chip32;
FindFirstNode();
chip32 << "\tjump .entry\r\n";
// First generate all constants
for (auto const nodeId : allNodeIds())
{
auto it = _models.find(nodeId);
if (it != _models.end())
{
chip32 << it->second->GenerateConstants() << "\n";
}
}
chip32 << ".entry:\r\n";
nlohmann::json nodesJsonArray;
for (auto const nodeId : allNodeIds())
{
auto it = _models.find(nodeId);
if (it == _models.end())
return "";
auto &model = it->second;
chip32 << model->Build() << "\n";
}
return chip32.str();
}
void StoryGraphModel::addPort(NodeId nodeId, PortType portType, PortIndex portIndex)
@ -497,6 +545,26 @@ void StoryGraphModel::removePort(NodeId nodeId, PortType portType, PortIndex por
portsDeleted();
}
QString StoryGraphModel::GetImagesDir() const
{
return QString(m_project.GetWorkingDir().c_str()) + QDir::separator() + "images";
}
QString StoryGraphModel::BuildFullImagePath(const QString &fileName) const
{
return GetImagesDir() + QDir::separator() + fileName;
}
QString StoryGraphModel::GetSoundsDir() const
{
return QString(m_project.GetWorkingDir().c_str()) + QDir::separator() + "sounds";
}
QString StoryGraphModel::BuildFullSoundPath(const QString &fileName) const
{
return GetSoundsDir() + QDir::separator() + fileName;
}
void StoryGraphModel::Clear()
{
_nodeIds.clear();

View file

@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2023-2099 Anthony Rabine <anthony@rabine.fr>
#pragma once
#include "src/story_project.h"
@ -10,6 +13,8 @@
#include <QtNodes/NodeDelegateModel>
#include <QtNodes/NodeDelegateModelRegistry>
#include <QAudioOutput>
#include <QMediaPlayer>
using QtNodes::ConnectionStyle;
using QtNodes::NodeRole;
@ -118,6 +123,10 @@ public:
}
StoryProject &GetProject() { return m_project; };
QString GetImagesDir() const;
QString GetSoundsDir() const;
QString BuildFullImagePath(const QString &fileName) const;
QString BuildFullSoundPath(const QString &fileName) const;
void Clear();
void SetInternalData(NodeId nodeId, nlohmann::json &j);
@ -129,7 +138,11 @@ public:
std::string Build();
std::string BuildNode(const NodeId nodeId) const;
// Centralized for wide usage
void PlaySound(const QString &fileName);
NodeId FindFirstNode() const;
signals:
void sigChooseFile(NodeId id, const QString &type);
@ -164,4 +177,7 @@ private:
/// A convenience variable needed for generating unique node ids.
unsigned int _nextNodeId{0};
QMediaPlayer *m_player{nullptr};
QAudioOutput *m_audioOutput{nullptr};
};

View file

@ -31,7 +31,7 @@ public:
}
void setNodeId(NodeId id) { m_nodeId = id; }
NodeId getNodeId() { return m_nodeId; }
NodeId getNodeId() const { return m_nodeId; }
virtual nlohmann::json ToJson() const {
nlohmann::json j;
@ -48,15 +48,21 @@ public:
// default impl
}
virtual std::string GenerateConstants() {
return "";
}
virtual std::string Build() {
return "";
}
NodeGeometryData &geometryData() { return m_geometryData; }
std::string GetNodeTitle() const { return m_nodeTitle; }
private:
NodeId m_nodeId;
std::string m_nodeTitle{"Media node"};
NodeGeometryData m_geometryData;
};

View file

@ -58,7 +58,9 @@ bool StoryProject::Load(const std::string &file_path, nlohmann::json &model)
m_name = projectData["name"].get<std::string>();
m_uuid = projectData["uuid"].get<std::string>();
nlohmann::json resourcesData = projectData["resources"];
if (j.contains("resources"))
{
nlohmann::json resourcesData = j["resources"];
for (const auto &obj : resourcesData)
{
@ -77,6 +79,7 @@ bool StoryProject::Load(const std::string &file_path, nlohmann::json &model)
success = true;
}
}
}
/*
@ -244,6 +247,15 @@ void StoryProject::ReplaceCharacter(std::string &theString, const std::string &t
while (found != std::string::npos);
}
std::string StoryProject::FileToConstant(const std::string &FileName)
{
std::string fileName = GetFileName(FileName);
std::string ext = GetFileExtension(fileName);
EraseString(fileName, "." + ext); // on retire l'extension du pack
return "$" + fileName + " DC8 \"" + fileName + "." + ext + "\", 8\r\n";
}
void StoryProject::AppendResource(const Resource &res)
{
m_resources.push_back(res);
@ -265,6 +277,14 @@ void StoryProject::ClearResources()
m_resources.clear();
}
void StoryProject::DeleteResourceAt(int index)
{
if ((index >= 0) && (index < m_resources.size()))
{
m_resources.erase(m_resources.begin() + index);
}
}
std::string StoryProject::GetFileExtension(const std::string &fileName)
{
if(fileName.find_last_of(".") != std::string::npos)

View file

@ -86,12 +86,13 @@ struct StoryProject
static std::string GetFileExtension(const std::string &FileName);
static std::string GetFileName(const std::string &path);
static void ReplaceCharacter(std::string &theString, const std::string &toFind, const std::string &toReplace);
static std::string FileToConstant(const std::string &FileName);
// ------------- Resources Management
void AppendResource(const Resource &res);
bool GetResourceAt(int index, Resource &resOut);
void ClearResources();
void DeleteResourceAt(int index);
int ResourcesSize() const { return m_resources.size(); }
std::vector<Resource>::const_iterator Begin() const { return m_resources.begin(); }

View file

@ -1,5 +1,6 @@
#include "toolbar.h"
#include "qmenubar.h"
#include "src/dock_widget_base.h"
#include <QMessageBox>
@ -108,6 +109,33 @@ void ToolBar::GenerateRecentProjectsMenu(const QStringList &recents)
}
}
QVariant ToolBar::GetDocksPreferences() const
{
QMap<QString, QVariant> prefs;
for (auto d : m_docksList)
{
prefs[d->objectName()] = d->GetPreferedVisibility();
}
return prefs;
}
QVariant ToolBar::SetDocksPreferences(const QVariant &prefs)
{
QMap<QString, QVariant> p = prefs.toMap();
for (auto d : m_docksList)
{
if (p.contains(d->objectName()))
{
d->SetPreferedVisibility(p[d->objectName()].toBool());
}
}
return prefs;
}
void ToolBar::SetActionsActive(bool enable)
{
for (auto d : m_actionDockList)
@ -128,8 +156,12 @@ void ToolBar::slotAbout()
"Build your own stories on an open source hardware."));
}
void ToolBar::AddDockToMenu(QAction *action)
void ToolBar::AddDockToMenu(QAction *action, DockWidgetBase *dock)
{
m_windowsMenu->addAction(action);
m_actionDockList.push_back(action);
m_docksList.push_back(dock);
connect(action, &QAction::triggered, this, [dock](bool checked) {
dock->SetPreferedVisibility(checked);
});
}

View file

@ -3,6 +3,7 @@
#include <QToolBar>
#include <QMenuBar>
#include "dock_widget_base.h"
class ToolBar : public QToolBar
{
@ -11,9 +12,11 @@ class ToolBar : public QToolBar
public:
ToolBar();
void createActions(QMenuBar *menuBar);
void AddDockToMenu(QAction *action);
void AddDockToMenu(QAction *action, DockWidgetBase *dock);
void SetActionsActive(bool enable);
void GenerateRecentProjectsMenu(const QStringList &recents);
QVariant GetDocksPreferences() const;
QVariant SetDocksPreferences(const QVariant &prefs);
signals:
void sigNew();
@ -35,6 +38,7 @@ private:
QAction *m_closeProjectAction{nullptr};
QAction *m_runAction{nullptr};
QList<QAction *> m_actionDockList;
QList<DockWidgetBase *> m_docksList;
};
#endif // TOOLBAR_H