Build binary, code display, errors management

This commit is contained in:
Anthony Rabine 2023-12-21 22:08:09 +01:00
parent 83d8542002
commit 09aab7241a
32 changed files with 657 additions and 522 deletions

View file

@ -94,15 +94,13 @@ static inline void leu16_put(std::vector<std::uint8_t> &container, uint16_t data
}
#define GET_REG(name, ra) if (!GetRegister(name, ra)) {\
std::stringstream ss; \
ss << "ERROR! Bad register name: " << name << std::endl;\
m_lastError = ss.str(); \
m_lastError.line -1; \
m_lastError.message = "ERROR! Bad register name: " + name; \
return false; }
#define CHIP32_CHECK(instr, cond, error) if (!(cond)) { \
std::stringstream ss; \
ss << "error line: " << instr.line << ": " << error << std::endl; \
m_lastError = ss.str(); \
m_lastError.line = instr.line; \
m_lastError.message = error; \
return false; } \
std::vector<std::string> Split(std::string line)
@ -242,7 +240,7 @@ bool Assembler::CompileMnemonicArguments(Instr &instr)
instr.compiledArgs.push_back(static_cast<uint32_t>(strtol(instr.args[2].c_str(), NULL, 0)));
break;
default:
CHIP32_CHECK(instr, false, "Unsupported mnemonic: " << instr.mnemonic);
CHIP32_CHECK(instr, false, "Unsupported mnemonic: " + instr.mnemonic);
break;
}
return true;
@ -286,7 +284,7 @@ bool Assembler::CompileConstantArgument(Instr &instr, const std::string &a)
((intVal <= UINT32_MAX) && (instr.dataTypeSize == 32))) {
sizeOk = true;
}
CHIP32_CHECK(instr, sizeOk, "integer too high: " << intVal);
CHIP32_CHECK(instr, sizeOk, "integer too high: " + std::to_string(intVal));
if (instr.dataTypeSize == 8) {
instr.compiledArgs.push_back(intVal);
} else if (instr.dataTypeSize == 16) {
@ -362,7 +360,7 @@ bool Assembler::Parse(const std::string &data)
instr.mnemonic = opcode;
instr.isLabel = true;
instr.addr = code_addr;
CHIP32_CHECK(instr, m_labels.count(opcode) == 0, "duplicated label : " << opcode);
CHIP32_CHECK(instr, m_labels.count(opcode) == 0, "duplicated label : " + opcode);
m_labels[opcode] = instr;
m_instructions.push_back(instr);
}
@ -383,7 +381,7 @@ bool Assembler::Parse(const std::string &data)
{
instr.args.insert(instr.args.begin(), lineParts.begin() + 1, lineParts.end());
CHIP32_CHECK(instr, instr.args.size() == instr.code.nbAargs,
"Bad number of parameters. Required: " << static_cast<int>(instr.code.nbAargs) << ", got: " << instr.args.size());
"Bad number of parameters. Required: " + std::to_string(static_cast<int>(instr.code.nbAargs)) + ", got: " + std::to_string(instr.args.size()));
nbArgsSuccess = true;
}
else
@ -412,7 +410,7 @@ bool Assembler::Parse(const std::string &data)
CHIP32_CHECK(instr, (type.size() >= 3), "bad data type size");
CHIP32_CHECK(instr, (type[0] == 'D') && ((type[1] == 'C') || (type[1] == 'V')), "bad data type (must be DCxx or DVxx");
CHIP32_CHECK(instr, m_labels.count(opcode) == 0, "duplicated label : " << opcode);
CHIP32_CHECK(instr, m_labels.count(opcode) == 0, "duplicated label : " + opcode);
instr.isRomData = type[1] == 'C' ? true : false;
instr.isRamData = type[1] == 'V' ? true : false;
@ -445,7 +443,8 @@ bool Assembler::Parse(const std::string &data)
}
else
{
m_lastError = "Unknown mnemonic or bad formatted line: " + std::to_string(lineNum);
m_lastError.message = "Unknown mnemonic or badly formatted line";
m_lastError.line = lineNum;
return false;
}
}
@ -458,7 +457,7 @@ bool Assembler::Parse(const std::string &data)
// label is the first argument for jump, second position for LCONS
uint16_t argsIndex = instr.code.opcode == OP_LCONS ? 1 : 0;
std::string label = instr.args[argsIndex];
CHIP32_CHECK(instr, m_labels.count(label) > 0, "label not found: " << label);
CHIP32_CHECK(instr, m_labels.count(label) > 0, "label not found: " + label);
uint16_t addr = m_labels[label].addr;
std::cout << "LABEL: " << label << " , addr: " << addr << std::endl;
instr.compiledArgs[argsIndex] = addr & 0xFF;

View file

@ -83,10 +83,16 @@ struct Result
}
};
class Assembler
{
public:
struct Error {
std::string message;
int line;
std::string ToString() const { return "Error line " + std::to_string(line) + ", " + message; }
};
// Separated parser to allow only code check
bool Parse(const std::string &data);
// Generate the executable binary after the parse pass
@ -104,7 +110,7 @@ public:
bool GetRegister(const std::string &regName, uint8_t &reg);
bool GetRegisterName(uint8_t reg, std::string &regName);
std::string GetLastError() { return m_lastError; }
Error GetLastError() { return m_lastError; }
private:
bool CompileMnemonicArguments(Instr &instr);
@ -112,7 +118,7 @@ private:
// label, address
std::map<std::string, Instr> m_labels;
std::string m_lastError;
Error m_lastError;
std::vector<Instr> m_instructions;
bool CompileConstantArgument(Instr &instr, const std::string &a);

View file

@ -119,8 +119,8 @@ set(SRCS
src/resources_window.cpp
src/resources_window.h
src/node_properties_window.cpp
src/node_properties_window.h
src/properties_window.cpp
src/properties_window.h
src/gui.h
src/gui.cpp
@ -128,6 +128,9 @@ set(SRCS
src/code_editor.cpp
src/code_editor.h
src/connection.cpp
src/connection.h
src/uuid.h
src/resource_manager.h
src/i_story_project.h

1
story-editor/icon.rc Normal file
View file

@ -0,0 +1 @@
IDI_ICON1 ICON DISCARDABLE "story-editor-logo.ico"

View file

@ -0,0 +1,53 @@
# define installer name
!define APPNAME "StoryEditor"
!define COMPANYNAME "OpenStoryTeller"
!define DESCRIPTION "A story editor using graphical nodes, for the OpenStoryTeller project. http://openstoryteller.org"
!define VERSIONMAJOR 1
!define VERSIONMINOR 3
!define VERSIONBUILD 4
OutFile "build/story-editor-setup.exe"
# set desktop as install directory
InstallDir "$PROGRAMFILES64\${APPNAME}"
Name "${COMPANYNAME} - ${APPNAME}"
# default section start
Section
# define output path
SetOutPath $INSTDIR
# specify file to go in output path
File /r "build/story-editor\*"
File "story-editor-logo.ico"
# define uninstaller name
WriteUninstaller $INSTDIR\uninstaller.exe
# Create shortcut
SetShellVarContext all
CreateDirectory "$SMPROGRAMS\${COMPANYNAME}"
CreateShortCut "$SMPROGRAMS\${COMPANYNAME}\${APPNAME}.lnk" "$INSTDIR\story-editor.exe" "" "$INSTDIR\story-editor-logo.ico"
SetShellVarContext current
#-------
# default section end
SectionEnd
# create a section to define what the uninstaller does.
# the section will always be named "Uninstall"
Section "Uninstall"
# Always delete uninstaller first
Delete $INSTDIR\uninstaller.exe
Delete "$SMPROGRAMS\${COMPANYNAME}\${APPNAME}.lnk"
Delete $INSTDIR\story-editor-logo.ico
# now delete installed file
Delete $INSTDIR\*
# Delete the directory
RMDir /r $INSTDIR
SectionEnd

View file

@ -105,8 +105,10 @@ public:
virtual void Draw() = 0;
virtual void DrawProperties() = 0;
virtual std::string GenerateConstants() = 0;
virtual std::string Build() = 0;
virtual std::string GetEntryLabel() = 0;
void SetPosition(float x, float y);
@ -135,8 +137,8 @@ public:
unsigned long GetId() const { return m_id; }
unsigned long GetInternalId() const { return m_node->ID.Get(); }
void seTitle(const std::string &title) { m_title = title; }
std::string getTitle() const { return m_title; }
void SeTitle(const std::string &title) { m_title = title; }
std::string GetTitle() const { return m_title; }
virtual void FromJson(const nlohmann::json &) = 0;
virtual void ToJson(nlohmann::json &) = 0;

View file

@ -1,6 +1,7 @@
#include "code_editor.h"
#include <fstream>
#include <memory>
CodeEditor::CodeEditor()
: WindowBase("Code editor")
@ -11,27 +12,32 @@ CodeEditor::CodeEditor()
void CodeEditor::Initialize()
{
// error markers
TextEditor::ErrorMarkers markers;
markers.insert(std::make_pair<int, std::string>(6, "Example error here:\nInclude file not found: \"TextEditor.h\""));
markers.insert(std::make_pair<int, std::string>(41, "Another example error"));
mEditor.SetErrorMarkers(markers);
//
// markers.insert(std::make_pair<int, std::string>(41, "Another example error"));
// "breakpoint" markers
//TextEditor::Breakpoints bpts;
//bpts.insert(24);
//bpts.insert(47);
//editor.SetBreakpoints(bpts);
m_breakPoints.insert(42);
mEditor.SetBreakpoints(m_breakPoints);
mFileToEdit = "test/test_zebra7500.js";
}
{
std::ifstream t(mFileToEdit);
if (t.good())
{
std::string str((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());
mEditor.SetText(str);
}
}
void CodeEditor::ClearErrors()
{
m_markers.clear();
}
void CodeEditor::AddError(int line, const std::string &text)
{
m_markers.insert(std::make_pair(line, text));
mEditor.SetErrorMarkers(m_markers);
}
void CodeEditor::SetScript(const std::string &txt)
{
mEditor.SetText(txt);
}
void CodeEditor::Draw()
@ -99,10 +105,9 @@ void CodeEditor::Draw()
ImGui::EndMenuBar();
}
ImGui::Text("%6d/%-6d %6d lines | %s | %s | %s | %s", cpos.mLine + 1, cpos.mColumn + 1, mEditor.GetTotalLines(),
ImGui::Text("%6d/%-6d %6d lines | %s | %s ", cpos.mLine + 1, cpos.mColumn + 1, mEditor.GetTotalLines(),
mEditor.IsOverwrite() ? "Ovr" : "Ins",
mEditor.CanUndo() ? "*" : " ",
mEditor.GetLanguageDefinition().mName.c_str(), mFileToEdit.c_str());
mEditor.CanUndo() ? "*" : " ");
mEditor.Render("TextEditor");
WindowBase::EndDraw();

View file

@ -11,7 +11,12 @@ public:
virtual void Draw() override;
void Initialize();
void SetScript(const std::string &txt);
void ClearErrors();
void AddError(int line, const std::string &text);
private:
TextEditor mEditor;
std::string mFileToEdit;
TextEditor::Breakpoints m_breakPoints;
TextEditor::ErrorMarkers m_markers;
};

View file

@ -0,0 +1,17 @@
#include "connection.h"
void to_json(nlohmann::json &j, const Connection &p) {
j = nlohmann::json{
{"outNodeId", static_cast<int64_t>(p.outNodeId)},
{"outPortIndex", static_cast<int64_t>(p.outPortIndex)},
{"inNodeId", static_cast<int64_t>(p.inNodeId)},
{"inPortIndex", static_cast<int64_t>(p.inPortIndex)},
};
}
void from_json(const nlohmann::json &j, Connection &p) {
j.at("outNodeId").get_to(p.outNodeId);
j.at("outPortIndex").get_to(p.outPortIndex);
j.at("inNodeId").get_to(p.inNodeId);
j.at("inPortIndex").get_to(p.inPortIndex);
}

View file

@ -0,0 +1,53 @@
#pragma once
#include <algorithm>
#include "json.hpp"
struct Connection
{
Connection()
: outNodeId(0)
, outPortIndex(0)
, inNodeId(0)
, inPortIndex(0)
{
}
unsigned int outNodeId{0};
unsigned int outPortIndex{0};
unsigned int inNodeId{0};
unsigned int inPortIndex{0};
Connection& operator=(const Connection& other) {
this->outNodeId = other.outNodeId;
this->outPortIndex = other.outPortIndex;
this->inNodeId = other.inNodeId;
this->inPortIndex = other.inPortIndex;
return *this;
}
};
inline bool operator==(Connection const &a, Connection const &b)
{
return a.outNodeId == b.outNodeId && a.outPortIndex == b.outPortIndex
&& a.inNodeId == b.inNodeId && a.inPortIndex == b.inPortIndex;
}
inline bool operator!=(Connection const &a, Connection const &b)
{
return !(a == b);
}
inline void invertConnection(Connection &id)
{
std::swap(id.outNodeId, id.inNodeId);
std::swap(id.outPortIndex, id.inPortIndex);
}
void to_json(nlohmann::json& j, const Connection& p);
void from_json(const nlohmann::json& j, Connection& p);

View file

@ -8,13 +8,7 @@ ConsoleWindow::ConsoleWindow()
{
ClearLog();
memset(InputBuf, 0, sizeof(InputBuf));
HistoryPos = -1;
// "CLASSIFY" is here to provide the test case where "C"+[tab] completes to "CL" and display multiple matches.
Commands.push_back("HELP");
Commands.push_back("HISTORY");
Commands.push_back("CLEAR");
Commands.push_back("CLASSIFY");
AutoScroll = true;
ScrollToBottom = false;
}
@ -22,8 +16,6 @@ ConsoleWindow::ConsoleWindow()
ConsoleWindow::~ConsoleWindow()
{
ClearLog();
for (int i = 0; i < History.Size; i++)
free(History[i]);
}
void ConsoleWindow::ClearLog()
@ -113,7 +105,16 @@ void ConsoleWindow::Draw()
{
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
{
ImGui::TextUnformatted(Items[i].c_str());
if (Items[i].type > 0)
{
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255,0,0,255));
}
ImGui::TextUnformatted(Items[i].text.c_str());
if (Items[i].type > 0)
{
ImGui::PopStyleColor();
}
}
}
/*
@ -168,151 +169,3 @@ void ConsoleWindow::Draw()
WindowBase::EndDraw();
}
void ConsoleWindow::ExecCommand(const char *command_line)
{
AddLog("# %s\n", command_line);
// Insert into history. First find match and delete it so it can be pushed to the back.
// This isn't trying to be smart or optimal.
HistoryPos = -1;
for (int i = History.Size - 1; i >= 0; i--)
if (Stricmp(History[i], command_line) == 0)
{
free(History[i]);
History.erase(History.begin() + i);
break;
}
History.push_back(Strdup(command_line));
// Process command
if (Stricmp(command_line, "CLEAR") == 0)
{
ClearLog();
}
else if (Stricmp(command_line, "HELP") == 0)
{
AddLog("Commands:");
for (int i = 0; i < Commands.Size; i++)
AddLog("- %s", Commands[i]);
}
else if (Stricmp(command_line, "HISTORY") == 0)
{
int first = History.Size - 10;
for (int i = first > 0 ? first : 0; i < History.Size; i++)
AddLog("%3d: %s\n", i, History[i]);
}
else
{
AddLog("Unknown command: '%s'\n", command_line);
}
// On command input, we scroll to bottom even if AutoScroll==false
ScrollToBottom = true;
}
int ConsoleWindow::TextEditCallbackStub(ImGuiInputTextCallbackData *data)
{
ConsoleWindow* console = (ConsoleWindow*)data->UserData;
return console->TextEditCallback(data);
}
int ConsoleWindow::TextEditCallback(ImGuiInputTextCallbackData *data)
{
//AddLog("cursor: %d, selection: %d-%d", data->CursorPos, data->SelectionStart, data->SelectionEnd);
switch (data->EventFlag)
{
case ImGuiInputTextFlags_CallbackCompletion:
{
// Example of TEXT COMPLETION
// Locate beginning of current word
const char* word_end = data->Buf + data->CursorPos;
const char* word_start = word_end;
while (word_start > data->Buf)
{
const char c = word_start[-1];
if (c == ' ' || c == '\t' || c == ',' || c == ';')
break;
word_start--;
}
// Build a list of candidates
ImVector<const char*> candidates;
for (int i = 0; i < Commands.Size; i++)
if (Strnicmp(Commands[i], word_start, (int)(word_end - word_start)) == 0)
candidates.push_back(Commands[i]);
if (candidates.Size == 0)
{
// No match
AddLog("No match for \"%.*s\"!\n", (int)(word_end - word_start), word_start);
}
else if (candidates.Size == 1)
{
// Single match. Delete the beginning of the word and replace it entirely so we've got nice casing.
data->DeleteChars((int)(word_start - data->Buf), (int)(word_end - word_start));
data->InsertChars(data->CursorPos, candidates[0]);
data->InsertChars(data->CursorPos, " ");
}
else
{
// Multiple matches. Complete as much as we can..
// So inputing "C"+Tab will complete to "CL" then display "CLEAR" and "CLASSIFY" as matches.
int match_len = (int)(word_end - word_start);
for (;;)
{
int c = 0;
bool all_candidates_matches = true;
for (int i = 0; i < candidates.Size && all_candidates_matches; i++)
if (i == 0)
c = toupper(candidates[i][match_len]);
else if (c == 0 || c != toupper(candidates[i][match_len]))
all_candidates_matches = false;
if (!all_candidates_matches)
break;
match_len++;
}
if (match_len > 0)
{
data->DeleteChars((int)(word_start - data->Buf), (int)(word_end - word_start));
data->InsertChars(data->CursorPos, candidates[0], candidates[0] + match_len);
}
// List matches
AddLog("Possible matches:\n");
for (int i = 0; i < candidates.Size; i++)
AddLog("- %s\n", candidates[i]);
}
break;
}
case ImGuiInputTextFlags_CallbackHistory:
{
// Example of HISTORY
const int prev_history_pos = HistoryPos;
if (data->EventKey == ImGuiKey_UpArrow)
{
if (HistoryPos == -1)
HistoryPos = History.Size - 1;
else if (HistoryPos > 0)
HistoryPos--;
}
else if (data->EventKey == ImGuiKey_DownArrow)
{
if (HistoryPos != -1)
if (++HistoryPos >= History.Size)
HistoryPos = -1;
}
// A better implementation would preserve the data on the current input line along with cursor position.
if (prev_history_pos != HistoryPos)
{
const char* history_str = (HistoryPos >= 0) ? History[HistoryPos] : "";
data->DeleteChars(0, data->BufTextLen);
data->InsertChars(0, history_str);
}
}
}
return 0;
}

View file

@ -25,42 +25,34 @@ public:
void ClearLog();
void AddMessage(const std::string &message) { AddLog("%s", message.c_str()); }
virtual void Draw() override;
void ExecCommand(const char* command_line);
// In C++11 you'd be better off using lambdas for this sort of forwarding callbacks
static int TextEditCallbackStub(ImGuiInputTextCallbackData* data);
int TextEditCallback(ImGuiInputTextCallbackData* data);
private:
void AddLog(const char* fmt, ...) IM_FMTARGS(2)
void AddLog(const std::string &text, uint32_t type)
{
// FIXME-OPT
char buf[1024];
va_list args;
va_start(args, fmt);
vsnprintf(buf, IM_ARRAYSIZE(buf), fmt, args);
buf[IM_ARRAYSIZE(buf)-1] = 0;
va_end(args);
Entry e{text, type};
std::scoped_lock<std::mutex> mutex(mLogMutex);
Items.push_back(Strdup(buf));
if (Items.size() > 100)
Items.push_back(e);
if (Items.size() > 100)
{
Items.erase(Items.begin());
}
}
virtual void Draw() override;
private:
struct Entry {
std::string text;
uint32_t type;
};
std::mutex mLogMutex;
char InputBuf[256];
std::vector<std::string> Items;
ImVector<const char*> Commands;
ImVector<char*> History;
int HistoryPos; // -1: new line, 0..History.Size-1 browsing history.
std::vector<Entry> Items;
ImGuiTextFilter Filter;
bool AutoScroll;
bool ScrollToBottom;

View file

@ -1,17 +1,18 @@
#include "emulator_window.h"
#include "gui.h"
#include "IconsMaterialDesignIcons.h"
EmulatorWindow::EmulatorWindow()
EmulatorWindow::EmulatorWindow(IStoryProject &proj)
: WindowBase("Emulator")
, m_project(proj)
{
Gui::LoadRawImage("assets/play.png", m_playImage);
}
void EmulatorWindow::Initialize() {
void EmulatorWindow::Initialize()
{
int my_image_width = 0;
int my_image_height = 0;
}
@ -33,8 +34,32 @@ void EmulatorWindow::Draw()
ImVec2 p = ImGui::GetCursorScreenPos();
ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + 320, p.y + 240), ImGui::GetColorU32(ImVec4(1.0, 1.0, 1.0, 1.0)));
ImGui::SetCursorScreenPos(ImVec2(p.x, p.y + 240));
ImGui::ImageButton("play", m_playImage.texture, ImVec2(45, 45));
ImGui::SetCursorScreenPos(ImVec2(p.x, p.y + 244));
float old_size = ImGui::GetFont()->Scale;
ImGui::GetFont()->Scale *= 2;
ImGui::PushFont(ImGui::GetFont());
ImGui::Button(ICON_MDI_PLAY_CIRCLE_OUTLINE, ImVec2(50, 50));
ImGui::SameLine();
ImGui::Button(ICON_MDI_STOP_CIRCLE_OUTLINE, ImVec2(50, 50));
ImGui::SameLine();
ImGui::Button(ICON_MDI_ARROW_LEFT_BOLD_CIRCLE_OUTLINE, ImVec2(50, 50));
ImGui::SameLine();
ImGui::Button(ICON_MDI_ARROW_RIGHT_BOLD_CIRCLE_OUTLINE, ImVec2(50, 50));
ImGui::GetFont()->Scale = old_size;
ImGui::PopFont();
ImGui::SeparatorText("Script control and debug");
if (ImGui::Button("Build"))
{
m_project.Build();
}
ImGui::SameLine();
WindowBase::EndDraw();
}

View file

@ -1,20 +1,17 @@
#pragma once
#include "window_base.h"
#include "gui.h"
#include "i_story_project.h"
class EmulatorWindow : public WindowBase
{
public:
EmulatorWindow();
EmulatorWindow(IStoryProject &proj);
void Initialize();
virtual void Draw() override;
private:
Gui::Image m_playImage;
Gui::Image m_pauseImage;
Gui::Image m_homeImage;
IStoryProject &m_project;
};

View file

@ -5,14 +5,17 @@
#include <string>
#include <vector>
#include <memory>
#include <list>
#include "resource.h"
#include "connection.h"
class IStoryProject
{
public:
virtual ~IStoryProject() {}
virtual void Log(const std::string &txt, bool critical = false) = 0;
virtual void PlaySoundFile(const std::string &fileName) = 0;
virtual std::string BuildFullAssetsPath(const std::string &fileName) const = 0;
@ -22,7 +25,12 @@ public:
virtual std::pair<FilterIterator, FilterIterator> Resources() = 0;
virtual void AddResource(std::shared_ptr<Resource> res) = 0;
virtual void ClearResources() = 0;
virtual void DeleteResource(FilterIterator &it) = 0;
// Node interaction
virtual void Build() = 0;
virtual std::list<std::shared_ptr<Connection>> GetNodeConnections(unsigned long nodeId) = 0;
virtual std::string GetNodeEntryLabel(unsigned long nodeId) = 0;
};

View file

@ -19,8 +19,10 @@
#include "ImGuiFileDialog.h"
MainWindow::MainWindow()
: m_resourcesWindow(*this)
: m_emulatorWindow(*this)
, m_resourcesWindow(*this)
, m_nodeEditorWindow(*this)
{
m_project.Clear();
}
@ -168,109 +170,15 @@ void MainWindow::Initialize()
gui.Initialize();
// gui.ApplyTheme();
editor.Initialize();
m_editor.Initialize();
m_emulatorWindow.Initialize();
m_nodeEditorWindow.Initialize();
m_nodePropertiesWindow.Initialize();
m_PropertiesWindow.Initialize();
LoadParams();
}
void MainWindow::ShowOptionsWindow()
{
static int pingState = 0;
// Always center this window when appearing
ImVec2 center = ImGui::GetMainViewport()->GetCenter();
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
ImGui::SetNextWindowSize(ImVec2(600, 0.0f));
if (ImGui::BeginPopupModal("Options", NULL, ImGuiWindowFlags_AlwaysAutoResize))
{
ImGui::PushItemWidth(-1.0f);
ImGui::Text("Adresse du serveur");
ImGui::SameLine();
ImGui::InputText("##addr", mBufAddress, sizeof(mBufAddress));
ImGui::Text("Chemin de récupération");
ImGui::SameLine();
ImGui::InputText("##rec_path", mBufReceivePath, sizeof(mBufReceivePath));
ImGui::Text("Chemin d'envoi des données");
ImGui::SameLine();
ImGui::InputText("##send_path", mBufSendPath, sizeof(mBufSendPath));
ImGui::PushItemWidth(100);
ImGui::Text("Port");
ImGui::SameLine();
ImGui::InputText("##port", mBufPort, sizeof(mBufPort), ImGuiInputTextFlags_CharsDecimal);
float width = 50;
ImGui::BeginGroup();
ImGui::PushID("Zebra7500");
ImGui::TextUnformatted("Adresse IP Zebra7500");
ImGui::SameLine();
for (int i = 0; i < 4; i++) {
ImGui::PushItemWidth(width);
ImGui::PushID(i);
bool invalid_octet = false;
if (octets[i] > 255) {
// Make values over 255 red, and when focus is lost reset it to 255.
octets[i] = 255;
invalid_octet = true;
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.0f, 0.0f, 1.0f));
}
if (octets[i] < 0) {
// Make values below 0 yellow, and when focus is lost reset it to 0.
octets[i] = 0;
invalid_octet = true;
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 0.0f, 1.0f));
}
ImGui::InputInt("##v", &octets[i], 0, 0, ImGuiInputTextFlags_CharsDecimal);
if (invalid_octet) {
ImGui::PopStyleColor();
}
ImGui::SameLine();
ImGui::PopID();
ImGui::PopItemWidth();
}
ImGui::PopID();
ImGui::EndGroup();
// Example action button and way to build the IP string
ImGui::SameLine();
ImGui::SameLine();
if (pingState == 1)
{
ImGui::TextUnformatted("Ping en cours...");
}
else if (pingState == 2)
{
ImGui::TextUnformatted("Ping succès!");
}
else if (pingState == 3)
{
ImGui::TextUnformatted("Ping erreur :(");
}
else
{
ImGui::TextUnformatted("");
}
ImGui::Separator();
ImGui::SetItemDefaultFocus();
ImGui::SameLine();
if (ImGui::Button("Cancel", ImVec2(120, 0)))
{
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}
bool MainWindow::ShowQuitConfirm()
{
@ -319,13 +227,13 @@ void MainWindow::OpenProjectDialog()
if (m_project.Load(filePathName, model, m_resources))
{
m_consoleWindow.AddMessage("Open project success");
Log("Open project success");
m_nodeEditorWindow.Load(model);
EnableProject();
}
else
{
m_consoleWindow.AddMessage("Open project error");
Log("Open project error");
}
@ -600,14 +508,12 @@ void MainWindow::Loop()
// ------------ Draw all windows
m_consoleWindow.Draw();
m_emulatorWindow.Draw();
editor.Draw();
m_editor.Draw();
m_resourcesWindow.Draw();
m_nodeEditorWindow.Draw();
m_nodePropertiesWindow.SetSelectedNode(m_nodeEditorWindow.GetSelectedNode());
m_nodePropertiesWindow.Draw();
ShowOptionsWindow();
m_PropertiesWindow.SetSelectedNode(m_nodeEditorWindow.GetSelectedNode());
m_PropertiesWindow.Draw();
NewProjectPopup();
OpenProjectDialog();
@ -629,9 +535,14 @@ void MainWindow::Loop()
gui.Destroy();
}
void MainWindow::Log(const std::string &txt, bool critical)
{
m_consoleWindow.AddLog(txt, critical ? 1 : 0);
}
void MainWindow::PlaySoundFile(const std::string &fileName)
{
m_consoleWindow.AddMessage("Play sound file: " + fileName);
Log("Play sound file: " + fileName);
m_project.PlaySoundFile(fileName);
}
@ -665,6 +576,112 @@ std::pair<FilterIterator, FilterIterator> MainWindow::Resources()
return m_resources.Items();
}
void MainWindow::DeleteResource(FilterIterator &it)
{
return m_resources.Delete(it);
}
void MainWindow::Build()
{
// 1. First compile nodes to assembly
CompileToAssembler();
// 2. Compile the assembly to machine binary
GenerateBinary();
// 3. Convert all media to desired type format
ConvertResources();
}
std::string MainWindow::GetNodeEntryLabel(unsigned long nodeId)
{
return m_nodeEditorWindow.GetNodeEntryLabel(nodeId);
}
std::list<std::shared_ptr<Connection>> MainWindow::GetNodeConnections(unsigned long nodeId)
{
return m_nodeEditorWindow.GetNodeConnections(nodeId);
}
bool MainWindow::CompileToAssembler()
{
// 1. Check if the model can be compiled, check for errors and report
// FIXME
// 2. Generate the assembly code from the model
m_currentCode = m_nodeEditorWindow.Build();
// Add global functions
{
std::string buffer;
std::ifstream f("scripts/media.asm");
f.seekg(0, std::ios::end);
buffer.resize(f.tellg());
f.seekg(0);
f.read(buffer.data(), buffer.size());
m_currentCode += buffer;
}
m_editor.SetScript(m_currentCode);
return true;
}
void MainWindow::GenerateBinary()
{
m_dbg.run_result = VM_FINISHED;
m_dbg.free_run = false;
if (m_assembler.Parse(m_currentCode) == true )
{
if (m_assembler.BuildBinary(m_program, m_result) == true)
{
m_result.Print();
Log("Binary successfully generated.");
// Update ROM memory
std::copy(m_program.begin(), m_program.end(), m_rom_data);
// FIXME
// m_ramView->SetMemory(m_ram_data, sizeof(m_ram_data));
// m_romView->SetMemory(m_rom_data, m_program.size());
m_project.SaveStory(m_program);
chip32_initialize(&m_chip32_ctx);
m_dbg.run_result = VM_OK;
UpdateVmView();
// DebugContext::DumpCodeAssembler(m_assembler);
}
else
{
Chip32::Assembler::Error err = m_assembler.GetLastError();
Log(err.ToString(), true);
m_editor.AddError(err.line, err.message); // show also the error in the code editor
}
}
else
{
Chip32::Assembler::Error err = m_assembler.GetLastError();
Log(err.ToString(), true);
m_editor.AddError(err.line, err.message); // show also the error in the code editor
}
}
void MainWindow::UpdateVmView()
{
// FIXME
// m_vmDock->updateRegistersView(m_chip32_ctx);
// highlightNextLine();
// Refresh RAM content
// m_ramView->SetMemory(m_ram_data, m_chip32_ctx.ram.size);
}
void MainWindow::ConvertResources()
{
}
void MainWindow::SaveParams()
{

View file

@ -8,7 +8,7 @@
#include "emulator_window.h"
#include "resources_window.h"
#include "node_editor_window.h"
#include "node_properties_window.h"
#include "properties_window.h"
#include "chip32_assembler.h"
#include "chip32_vm.h"
@ -92,7 +92,8 @@ private:
std::vector<uint8_t> m_program;
Chip32::Assembler m_assembler;
Chip32::Result m_result;
// DebugContext m_dbg;
DebugContext m_dbg;
std::string m_currentCode;
std::vector<std::string> m_recentProjects;
@ -101,29 +102,17 @@ private:
Gui gui;
EmulatorWindow m_emulatorWindow;
ConsoleWindow m_consoleWindow;
CodeEditor editor;
CodeEditor m_editor;
ResourcesWindow m_resourcesWindow;
NodeEditorWindow m_nodeEditorWindow;
NodePropertiesWindow m_nodePropertiesWindow;
char mBufAddress[200];
char mBufReceivePath[200];
char mBufSendPath[200];
char mBufPort[10];
int octets[4];
std::string mServerAddr;
std::string mServerRecUrl;
std::string mServerSndUrl;
int mServerPort;
PropertiesWindow m_PropertiesWindow;
// From IStoryProject (proxy to StoryProject class)
virtual void Log(const std::string &txt, bool critical = false) override;
virtual void PlaySoundFile(const std::string &fileName) override;;
virtual std::string BuildFullAssetsPath(const std::string &fileName) const override;
virtual std::pair<FilterIterator, FilterIterator> Images() override;
@ -132,13 +121,15 @@ private:
virtual void AddResource(std::shared_ptr<Resource> res) override;
virtual void ClearResources() override;
virtual std::pair<FilterIterator, FilterIterator> Resources() override;
virtual void DeleteResource(FilterIterator &it) override;
virtual void Build() override;
virtual std::list<std::shared_ptr<Connection>> GetNodeConnections(unsigned long nodeId) override;
virtual std::string GetNodeEntryLabel(unsigned long nodeId) override;
void SaveParams();
void LoadParams();
void DrawMainMenuBar();
void ShowOptionsWindow();
bool ShowQuitConfirm();
void NewProjectPopup();
@ -147,6 +138,11 @@ private:
void CloseProject();
void OpenProjectDialog();
void DrawStatusBar();
bool CompileToAssembler();
void ConvertResources();
void GenerateBinary();
void UpdateVmView();
};
#endif // MAINWINDOW_H

View file

@ -2,7 +2,7 @@
namespace ed = ax::NodeEditor;
#include "IconsMaterialDesignIcons.h"
#include "story_project.h"
MediaNode::MediaNode(const std::string &title, IStoryProject &proj)
: BaseNode(title, proj)
@ -128,6 +128,11 @@ void MediaNode::DrawProperties()
ImGui::OpenPopup("popup_button");
isImage = true;
}
ImGui::SameLine();
if (ImGui::Button(ICON_MDI_CLOSE_BOX_OUTLINE "##delimage")) {
SetImage("");
}
ImGui::AlignTextToFramePadding();
ImGui::Text("Sound");
@ -149,6 +154,11 @@ void MediaNode::DrawProperties()
isImage = false;
}
ImGui::SameLine();
if (ImGui::Button(ICON_MDI_CLOSE_BOX_OUTLINE "##delsound")) {
SetSound("");
}
// This is the actual popup Gui drawing section.
if (ImGui::BeginPopup("popup_button")) {
ImGui::SeparatorText(isImage ? "Images" : "Sounds");
@ -189,38 +199,36 @@ void MediaNode::SetSound(const std::string &f)
/*
std::string NodeEditorWindow::ChoiceLabel() const
std::string MediaNode::ChoiceLabel() const
{
std::stringstream ss;
ss << "mediaChoice" << std::setw(4) << std::setfill('0') << GetId();
return ss.str();
}
std::string NodeEditorWindow::EntryLabel() const
std::string MediaNode::GetEntryLabel()
{
std::stringstream ss;
ss << ".mediaEntry" << std::setw(4) << std::setfill('0') << getNodeId();
ss << ".mediaEntry" << std::setw(4) << std::setfill('0') << GetId();
return ss.str();
}
std::string NodeEditorWindow::GenerateConstants()
std::string MediaNode::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)
if (m_image.name.size() > 0)
{
s = StoryProject::FileToConstant(image, ".qoi"); // FIXME: Generate the extension setup in user option of output format
s = StoryProject::FileToConstant(m_image.name, ".qoi"); // FIXME: Generate the extension setup in user option of output format
}
if (sound.size() > 0)
if (m_soundName.size() > 0)
{
s += StoryProject::FileToConstant(sound, ".wav"); // FIXME: Generate the extension setup in user option of output format
s += StoryProject::FileToConstant(m_soundName, ".wav"); // FIXME: Generate the extension setup in user option of output format
}
int nb_out_conns = ComputeOutputConnections();
int nb_out_conns = Outputs();
if (nb_out_conns > 1)
{
// Generate choice table if needed (out ports > 1)
@ -230,14 +238,14 @@ std::string NodeEditorWindow::GenerateConstants()
<< " DC32, "
<< nb_out_conns << ", ";
std::unordered_set<ConnectionId> conns = m_model.allConnectionIds(getNodeId());
std::list<std::shared_ptr<Connection>> conns = m_project.GetNodeConnections(GetId());
int i = 0;
for (auto & c : conns)
{
std::stringstream ssChoice;
// On va chercher le label d'entrée du noeud connecté à l'autre bout
ss << m_model.GetNodeEntryLabel(c.inNodeId);
ss << m_project.GetNodeEntryLabel(c->inNodeId);
if (i < (nb_out_conns - 1))
{
ss << ", ";
@ -255,22 +263,22 @@ std::string NodeEditorWindow::GenerateConstants()
return s;
}
std::string NodeEditorWindow::Build()
std::string MediaNode::Build()
{
std::stringstream ss;
int nb_out_conns = ComputeOutputConnections();
int nb_out_conns = Outputs();
ss << R"(; ---------------------------- )"
<< GetNodeTitle()
<< GetTitle()
<< " Type: "
<< (nb_out_conns == 0 ? "End" : nb_out_conns == 1 ? "Transition" : "Choice")
<< "\n";
std::string image = StoryProject::RemoveFileExtension(m_mediaData["image"].get<std::string>());
std::string sound = StoryProject::RemoveFileExtension(m_mediaData["sound"].get<std::string>());
std::string image = StoryProject::RemoveFileExtension(m_image.name);
std::string sound = StoryProject::RemoveFileExtension(m_soundName);
// Le label de ce noeud est généré de la façon suivante :
// "media" + Node ID + id du noeud parent. Si pas de noeud parent, alors rien
ss << EntryLabel() << ":\n";
ss << GetEntryLabel() << ":\n";
if (image.size() > 0)
{
@ -301,18 +309,18 @@ std::string NodeEditorWindow::Build()
{
ss << "halt\n";
}
else if (nb_out_conns == 1) // Transition node
else if (nb_out_conns == 1) // it is a transition node
{
std::unordered_set<ConnectionId> conns = m_model.allConnectionIds(getNodeId());
std::list<std::shared_ptr<Connection>> conns = m_project.GetNodeConnections(GetId());
for (auto c : conns)
{
if (c.outNodeId == getNodeId())
if (c->outNodeId == GetId())
{
// On place dans R0 le prochain noeud à exécuter en cas de OK
ss << "lcons r0, "
<< m_model.GetNodeEntryLabel(c.inNodeId) << "\n"
<< m_project.GetNodeEntryLabel(c->inNodeId) << "\n"
<< "ret\n";
}
}
@ -325,4 +333,4 @@ std::string NodeEditorWindow::Build()
}
return ss.str();
}
*/

View file

@ -20,9 +20,10 @@ public:
virtual void FromJson(const nlohmann::json &j) override;
virtual void ToJson(nlohmann::json &j) override;
virtual void DrawProperties() override;
virtual std::string Build() override;
virtual std::string GetEntryLabel() override;
virtual std::string GenerateConstants() override;
private:
IStoryProject &m_project;
Gui::Image m_image;
@ -34,4 +35,5 @@ private:
std::string m_buttonUniqueName;
void SetImage(const std::string &f);
void SetSound(const std::string &f);
std::string ChoiceLabel() const;
};

View file

@ -57,7 +57,7 @@ void NodeEditorWindow::LoadNode(const nlohmann::json &nodeJson)
n->SetPosition(posJson["x"].get<float>(), posJson["y"].get<float>());
n->FromJson(internalDataJson);
m_nodes[n->GetInternalId()] = n;
m_nodes.push_back(n);
}
else
{
@ -78,9 +78,9 @@ ed::PinId NodeEditorWindow::GetInputPin(unsigned long modelNodeId, int pinIndex)
for (auto & n : m_nodes)
{
if (n.second->GetId() == modelNodeId)
if (n->GetId() == modelNodeId)
{
id = n.second->GetInputPinAt(pinIndex);
id = n->GetInputPinAt(pinIndex);
}
}
@ -98,9 +98,9 @@ ed::PinId NodeEditorWindow::GetOutputPin(unsigned long modelNodeId, int pinIndex
for (auto & n : m_nodes)
{
if (n.second->GetId() == modelNodeId)
if (n->GetId() == modelNodeId)
{
id = n.second->GetOutputPinAt(pinIndex);
id = n->GetOutputPinAt(pinIndex);
}
}
@ -133,13 +133,13 @@ void NodeEditorWindow::Load(const nlohmann::json &model)
auto conn = std::make_shared<LinkInfo>();
// our model
conn->model = connection.get<Connection>();
*conn->model = connection.get<Connection>();
// ImGui stuff for links
conn->Id = BaseNode::GetNextId();
conn->InputId = GetInputPin(conn->model.inNodeId, conn->model.inPortIndex);
conn->OutputId = GetOutputPin(conn->model.outNodeId, conn->model.outPortIndex);
conn->ed_link->Id = BaseNode::GetNextId();
conn->ed_link->InputId = GetInputPin(conn->model->inNodeId, conn->model->inPortIndex);
conn->ed_link->OutputId = GetOutputPin(conn->model->outNodeId, conn->model->outPortIndex);
// Since we accepted new link, lets add one to our list of links.
m_links.push_back(conn);
@ -155,18 +155,18 @@ void NodeEditorWindow::Save(nlohmann::json &model)
for (const auto & n : m_nodes)
{
nlohmann::json node;
node["id"] = n.second->GetId();
node["type"] = n.second->GetType();
node["outPortCount"] = n.second->Outputs();
node["inPortCount"] = n.second->Inputs();
node["id"] = n->GetId();
node["type"] = n->GetType();
node["outPortCount"] = n->Outputs();
node["inPortCount"] = n->Inputs();
nlohmann::json position;
position["x"] = n.second->GetX();
position["y"] = n.second->GetY();
position["x"] = n->GetX();
position["y"] = n->GetY();
nlohmann::json internalData;
n.second->ToJson(internalData);
n->ToJson(internalData);
node["position"] = position;
node["internal-data"] = internalData;
@ -185,27 +185,116 @@ void NodeEditorWindow::Save(nlohmann::json &model)
int index;
for (const auto & n : m_nodes)
{
if (n.second->HasOnputPinId(linkInfo->OutputId, index))
if (n->HasOnputPinId(linkInfo->ed_link->OutputId, index))
{
c["outNodeId"] = n.second->GetId();
c["outNodeId"] = n->GetId();
c["outPortIndex"] = index;
}
if (n.second->HasInputPinId(linkInfo->InputId, index))
if (n->HasInputPinId(linkInfo->ed_link->InputId, index))
{
c["inNodeId"] = n.second->GetId();
c["inNodeId"] = n->GetId();
c["inPortIndex"] = index;
}
}
connections.push_back(c);
ed::Link(linkInfo->Id, linkInfo->OutputId, linkInfo->InputId);
}
model["connections"] = connections;
ed::SetCurrentEditor(nullptr);
}
uint32_t NodeEditorWindow::FindFirstNode() const
{
uint32_t id = 0;
// First node is the one without connection on its input port
for (const auto & n : m_nodes)
{
bool foundConnection = false;
for (const auto& l : m_links)
{
if (l->model->inNodeId == n->GetId())
{
foundConnection = true;
}
}
if (!foundConnection)
{
id = n->GetId();
m_project.Log("First node is: " + std::to_string(id));
break;
}
}
return id;
}
std::string NodeEditorWindow::Build()
{
std::stringstream code;
ed::SetCurrentEditor(m_context);
std::stringstream chip32;
uint32_t firstNode = FindFirstNode();
code << "\tjump " << GetNodeEntryLabel(firstNode) << "\r\n";
// First generate all constants
for (const auto & n : m_nodes)
{
code << n->GenerateConstants() << "\n";
}
for (const auto & n : m_nodes)
{
code << n->Build() << "\n";
}
ed::SetCurrentEditor(nullptr);
return code.str();
}
std::list<std::shared_ptr<Connection>> NodeEditorWindow::GetNodeConnections(unsigned long nodeId)
{
std::list<std::shared_ptr<Connection>> c;
ed::SetCurrentEditor(m_context);
for (const auto & l : m_links)
{
if (l->model->outNodeId == nodeId)
{
c.push_back(l->model);
}
}
ed::SetCurrentEditor(nullptr);
return c;
}
std::string NodeEditorWindow::GetNodeEntryLabel(unsigned long nodeId)
{
std::string label;
ed::SetCurrentEditor(m_context);
for (const auto & n : m_nodes)
{
if (n->GetId() == nodeId)
{
label = n->GetEntryLabel();
break;
}
}
ed::SetCurrentEditor(nullptr);
return label;
}
std::shared_ptr<BaseNode> NodeEditorWindow::GetSelectedNode()
{
@ -219,9 +308,12 @@ std::shared_ptr<BaseNode> NodeEditorWindow::GetSelectedNode()
if (nodeCount > 0)
{
if (m_nodes.contains(nId.Get()))
for (auto & n : m_nodes)
{
selected = m_nodes[nId.Get()];
if (n->GetInternalId() == nId.Get())
{
selected = n;
}
}
}
}
@ -242,14 +334,14 @@ void NodeEditorWindow::Draw()
for (const auto & n : m_nodes)
{
ImGui::PushID(n.first);
n.second->Draw();
ImGui::PushID(n->GetInternalId());
n->Draw();
ImGui::PopID();
}
for (const auto& linkInfo : m_links)
{
ed::Link(linkInfo->Id, linkInfo->OutputId, linkInfo->InputId);
ed::Link(linkInfo->ed_link->Id, linkInfo->ed_link->OutputId, linkInfo->ed_link->InputId);
}
// Handle creation action, returns true if editor want to create new object (node or link)
@ -304,7 +396,10 @@ void NodeEditorWindow::Draw()
m_links.erase(std::remove_if(m_links.begin(),
m_links.end(),
[deletedLinkId](std::shared_ptr<LinkInfo> inf) { return inf->Id == deletedLinkId; }));
[deletedLinkId](std::shared_ptr<LinkInfo> inf)
{
return inf->ed_link->Id == deletedLinkId;
}));
}
// You may reject link deletion by calling:

View file

@ -28,15 +28,24 @@ namespace ed = ax::NodeEditor;
class NodeEditorWindow : public WindowBase
{
public:
struct LinkInfo
{
// Stuff from ImGuiNodeEditor
struct EditorLink {
ed::LinkId Id;
ed::PinId InputId;
ed::PinId OutputId;
};
// Stuff from the project.json file, our model
Connection model;
// Stuff from ImGuiNodeEditor, each element has a unique ID within one editor
struct LinkInfo
{
LinkInfo()
{
ed_link = std::make_shared<EditorLink>();
model = std::make_shared<Connection>();
}
std::shared_ptr<EditorLink> ed_link;
std::shared_ptr<Connection> model;
};
NodeEditorWindow(IStoryProject &proj);
@ -47,17 +56,20 @@ public:
void Clear();
void Load(const nlohmann::json &model);
void Save(nlohmann::json &model);
std::string Build();
std::list<std::shared_ptr<Connection> > GetNodeConnections(unsigned long nodeId);
std::string GetNodeEntryLabel(unsigned long nodeId);
std::shared_ptr<BaseNode> GetSelectedNode();
std::string GenerateConstants();
private:
IStoryProject &m_project;
ed::EditorContext* m_context = nullptr;
// key: Id
std::map<unsigned long, std::shared_ptr<BaseNode>> m_nodes;
std::vector<std::shared_ptr<LinkInfo>> m_links; // List of live links. It is dynamic unless you want to create read-only view over nodes.
std::list<std::shared_ptr<BaseNode>> m_nodes;
std::list<std::shared_ptr<LinkInfo>> m_links; // List of live links. It is dynamic unless you want to create read-only view over nodes.
void ToolbarUI();
@ -104,7 +116,6 @@ private:
void LoadNode(const nlohmann::json &nodeJson);
ed::PinId GetInputPin(unsigned long modelNodeId, int pinIndex);
ed::PinId GetOutputPin(unsigned long modelNodeId, int pinIndex);
std::string ChoiceLabel() const;
std::string EntryLabel() const;
uint32_t FindFirstNode() const;
};

View file

@ -1,42 +0,0 @@
#include "node_properties_window.h"
#include "gui.h"
NodePropertiesWindow::NodePropertiesWindow()
: WindowBase("Properties")
{
}
void NodePropertiesWindow::Initialize() {
int my_image_width = 0;
int my_image_height = 0;
}
void NodePropertiesWindow::Draw()
{
// if (!IsVisible())
// {
// return;
// }
WindowBase::BeginDraw();
ImGui::SetWindowSize(ImVec2(626, 744), ImGuiCond_FirstUseEver);
static char buf1[32] = ""; ImGui::InputText("Title", buf1, 32);
if (m_selectedNode)
{
m_selectedNode->DrawProperties();
}
WindowBase::EndDraw();
}
void NodePropertiesWindow::SetSelectedNode(std::shared_ptr<BaseNode> node)
{
m_selectedNode = node;
}

View file

@ -0,0 +1,47 @@
#include "properties_window.h"
#include "gui.h"
PropertiesWindow::PropertiesWindow()
: WindowBase("Properties")
{
}
void PropertiesWindow::Initialize() {
int my_image_width = 0;
int my_image_height = 0;
}
void PropertiesWindow::Draw()
{
// if (!IsVisible())
// {
// return;
// }
WindowBase::BeginDraw();
ImGui::SetWindowSize(ImVec2(626, 744), ImGuiCond_FirstUseEver);
ImGui::SeparatorText("Project");
ImGui::SeparatorText("Selected node");
if (m_selectedNode)
{
static char buf1[32] = ""; ImGui::InputText("Title", buf1, 32);
ImGui::Text("Node ID: %lu", m_selectedNode->GetId());
m_selectedNode->DrawProperties();
}
WindowBase::EndDraw();
}
void PropertiesWindow::SetSelectedNode(std::shared_ptr<BaseNode> node)
{
m_selectedNode = node;
}

View file

@ -5,10 +5,10 @@
#include "base_node.h"
class NodePropertiesWindow : public WindowBase
class PropertiesWindow : public WindowBase
{
public:
NodePropertiesWindow();
PropertiesWindow();
void Initialize();
virtual void Draw() override;

View file

@ -5,6 +5,7 @@
#include <string>
#include <vector>
#include <iostream>
#include <list>
struct Resource
{
@ -20,11 +21,8 @@ struct Resource
// Itérateur pour parcourir les éléments filtrés
class FilterIterator {
private:
using Iterator = std::vector<std::shared_ptr<Resource>>::const_iterator;
Iterator current;
Iterator end;
std::string filterType;
public:
using Iterator = std::list<std::shared_ptr<Resource>>::const_iterator;
public:
FilterIterator(Iterator start, Iterator end, const std::string &type)
@ -37,6 +35,10 @@ public:
return *current;
}
const Iterator Current() const {
return current;
}
// Surcharge de l'opérateur d'incrémentation
FilterIterator& operator++() {
++current;
@ -55,6 +57,10 @@ public:
}
private:
Iterator current;
Iterator end;
std::string filterType;
// Fonction pour trouver le prochain élément qui correspond au filtre
void searchNext() {

View file

@ -30,6 +30,11 @@ public:
UpdateIterators();
}
void Delete(FilterIterator &it)
{
m_items.erase(it.Current());
}
void Clear()
{
m_items.clear();
@ -61,7 +66,7 @@ public:
private:
std::vector<std::shared_ptr<Resource>> m_items;
std::list<std::shared_ptr<Resource>> m_items;
std::pair<FilterIterator, FilterIterator> m_images;
std::pair<FilterIterator, FilterIterator> m_sounds;

View file

@ -109,6 +109,7 @@ void ResourcesWindow::Draw()
int id = 1000;
for (auto it = b; it != e; ++it)
{
bool quitLoop = false;
ImGui::PushID(id);
ImGui::TableNextColumn();
ImGui::Text("%s", (*it)->file.c_str());
@ -132,7 +133,6 @@ void ResourcesWindow::Draw()
strncpy(description, (*it)->description.c_str(), sizeof(description));
init = false;
}
// ImGui::PushID(id);
ImGui::InputText("Description", description, sizeof(description));
if (ImGui::Button("Close"))
{
@ -140,7 +140,6 @@ void ResourcesWindow::Draw()
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
// ImGui::PopID();
}
ImGui::SameLine();
ImGui::Text("%s", (*it)->description.c_str());
@ -153,9 +152,16 @@ void ResourcesWindow::Draw()
ImGui::TableNextColumn();
if (ImGui::SmallButton("Delete"))
{
m_project.DeleteResource(it);
quitLoop = true;
}
ImGui::PopID();
id++;
if (quitLoop)
{
break;
}
}
ImGui::EndTable();

View file

@ -422,18 +422,4 @@ std::string StoryProject::BuildFullAssetsPath(const std::string &fileName) const
}
void to_json(nlohmann::json &j, const Connection &p) {
j = nlohmann::json{
{"outNodeId", static_cast<int64_t>(p.outNodeId)},
{"outPortIndex", static_cast<int64_t>(p.outPortIndex)},
{"inNodeId", static_cast<int64_t>(p.inNodeId)},
{"inPortIndex", static_cast<int64_t>(p.inPortIndex)},
};
}
void from_json(const nlohmann::json &j, Connection &p) {
j.at("outNodeId").get_to(p.outNodeId);
j.at("outPortIndex").get_to(p.outPortIndex);
j.at("inNodeId").get_to(p.inNodeId);
j.at("inPortIndex").get_to(p.inPortIndex);
}

View file

@ -48,35 +48,6 @@ struct AudioCommand {
};
struct Connection
{
int outNodeId;
int outPortIndex;
int inNodeId;
int inPortIndex;
};
inline bool operator==(Connection const &a, Connection const &b)
{
return a.outNodeId == b.outNodeId && a.outPortIndex == b.outPortIndex
&& a.inNodeId == b.inNodeId && a.inPortIndex == b.inPortIndex;
}
inline bool operator!=(Connection const &a, Connection const &b)
{
return !(a == b);
}
inline void invertConnection(Connection &id)
{
std::swap(id.outNodeId, id.inNodeId);
std::swap(id.outPortIndex, id.inPortIndex);
}
void to_json(nlohmann::json& j, const Connection& p);
void from_json(const nlohmann::json& j, Connection& p);
// FIXME : Structure très Lunii style, à utiliser pour la conversion peut-être ...
struct StoryNode

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View file

@ -0,0 +1,8 @@
[Desktop Entry]
Type=Application
Name=StoryEditor
Comment=A tool to create stories with graphical nodes (OpenStoryTeller project)
Exec=story-editor
Icon=story-editor-logo
Categories=Graphics;2DGraphics;