Commit 3f4971b6 authored by Gauthier Quesnel's avatar Gauthier Quesnel
Browse files

gui: enable i/o operation

parent a91f2c26
Pipeline #11168 failed with stage
in 1 minute and 2 seconds
......@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
project(irritator-gui VERSION 0.1.0 LANGUAGES CXX)
set(gui_sources
imnodes.cpp imnodes.hpp window-logger.cpp node-editor.cpp
dialog-file.cpp imnodes.cpp imnodes.hpp window-logger.cpp node-editor.cpp
${PROJECT_SOURCE_DIR}/../../external/imgui/imgui.cpp
${PROJECT_SOURCE_DIR}/../../external/imgui/imgui.h
${PROJECT_SOURCE_DIR}/../../external/imgui/imgui_demo.cpp
......
// Copyright (c) 2020 INRA Distributed under the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include "gui.hpp"
#include <fmt/format.h>
#include <algorithm>
#include <array>
#include <filesystem>
#include <vector>
#include <windows.h>
namespace irt {
struct file_dialog
{
std::vector<std::filesystem::path> paths;
std::filesystem::path current;
std::filesystem::path selected;
std::string temp;
char buffer[128];
bool is_open = true;
#ifdef _WIN32
uint32_t drives{ 0 };
void fill_drives()
{
DWORD mask = ::GetLogicalDrives();
uint32_t ret = { 0 };
for (int i = 0; i < 26; ++i) {
if (!(mask & (1 << i)))
continue;
char rootName[4] = { static_cast<char>('A' + i), ':', '\\', '\0' };
UINT type = ::GetDriveTypeA(rootName);
if (type == DRIVE_REMOVABLE || type == DRIVE_FIXED)
ret |= (1 << i);
}
drives = ret;
}
#endif
const char** file_filters;
const char** extension_filters;
bool have_good_file_name_starts(const std::filesystem::path& p)
{
if (file_filters == nullptr)
return true;
const char** filters = file_filters;
while (*filters) {
if (p.string().starts_with(*filters))
return true;
++filters;
}
return false;
}
bool have_good_extension(const std::filesystem::path& p)
{
if (extension_filters == nullptr)
return true;
const char** filters = extension_filters;
while (*filters) {
if (p.extension().string() == *filters)
return true;
++filters;
}
return false;
}
void copy_files_and_directories(const std::filesystem::path& current_path)
{
for (std::filesystem::directory_iterator it(current_path), et; it != et;
++it) {
std::error_code err;
if (it->is_directory(err) && !err) {
paths.emplace_back(*it);
continue;
}
if (it->is_regular_file(err) && !err) {
if (have_good_extension(*it) &&
have_good_file_name_starts(*it)) {
paths.emplace_back(*it);
continue;
}
}
}
}
void sort()
{
std::sort(std::begin(paths),
std::end(paths),
[](const auto& lhs, const auto& rhs) {
if (std::filesystem::is_directory(lhs)) {
if (std::filesystem::is_directory(rhs))
return lhs.filename() < rhs.filename();
return true;
}
if (std::filesystem::is_directory(rhs))
return false;
return lhs.filename() < rhs.filename();
});
}
void clear()
{
paths.clear();
selected.clear();
current.clear();
}
void show_drives([[maybe_unused]] bool* path_click,
[[maybe_unused]] std::filesystem::path* next)
{
#ifdef _WIN32
char current_drive = static_cast<char>(current.c_str()[0]);
char drive_string[] = { current_drive, ':', '\0' };
ImGui::PushItemWidth(4 * ImGui::GetFontSize());
if (ImGui::BeginCombo("##select_win_drive", drive_string)) {
for (int i = 0; i < 26; ++i) {
if (!(drives & (1 << i)))
continue;
char drive_char = static_cast<char>('A' + i);
char selectable_string[] = { drive_char, ':', '\0' };
bool is_selected = current_drive == drive_char;
if (ImGui::Selectable(selectable_string, is_selected) &&
!is_selected) {
char new_current[] = { drive_char, ':', '\\', '\0' };
std::error_code err;
std::filesystem::current_path(new_current, err);
if (!err) {
selected.clear();
*path_click = true;
*next = std::filesystem::current_path(err);
}
}
}
ImGui::EndCombo();
}
ImGui::PopItemWidth();
ImGui::SameLine();
#endif
}
void show_path(bool* path_click, std::filesystem::path* next)
{
for (auto it = current.begin(), et = current.end(); it != et; ++it) {
if (it != current.begin())
ImGui::SameLine();
if (ImGui::Button(it->string().c_str())) {
next->clear();
for (auto jt = current.begin(); jt != it; ++jt)
*next /= jt->native();
*next /= it->native();
selected.clear();
*path_click = true;
break;
}
}
}
};
file_dialog fd;
bool
load_file_dialog(const char* description,
const char* filters[],
std::filesystem::path& out)
{
if (fd.current.empty()) {
fd.fill_drives();
fd.selected.clear();
std::error_code error;
fd.current = std::filesystem::current_path(error);
}
std::filesystem::path next;
bool res = false;
if (ImGui::BeginPopupModal("Select file path to load")) {
bool path_click = false;
fd.show_drives(&path_click, &next);
if (!path_click)
fd.show_path(&path_click, &next);
if (!path_click) {
ImVec2 size = ImGui::GetContentRegionMax();
size.y /= 1.5;
ImGui::BeginChild("##select_files", size);
if (ImGui::Selectable("..##select_file", (fd.selected == ".."))) {
if (next.empty()) {
next = fd.current.parent_path();
fd.selected.clear();
path_click = true;
}
}
for (auto it = fd.paths.begin(), et = fd.paths.end(); it != et;
++it) {
fd.temp.clear();
if (std::filesystem::is_directory(*it))
fmt::format_to(std::back_inserter(fd.temp),
"[Dir] {}",
it->filename().string());
else
fd.temp = it->filename().string();
if (ImGui::Selectable(fd.temp.c_str(),
(it->filename() == fd.selected))) {
fd.selected = it->filename();
if (std::filesystem::is_directory(*it)) {
if (next.empty()) {
fd.selected.clear();
next = fd.current;
next /= it->filename();
path_click = true;
}
}
break;
}
}
ImGui::EndChild();
}
if (path_click) {
fd.paths.clear();
static const char* filters[] = { ".irt", nullptr };
fd.extension_filters = filters;
fd.file_filters = nullptr;
fd.copy_files_and_directories(next);
fd.sort();
fd.current = next;
}
ImGui::Text("File Name: %s", fd.selected.string().c_str());
float width = ImGui::GetContentRegionAvailWidth();
ImGui::PushItemWidth(width);
if (ImGui::Button("Ok", ImVec2(width / 2, 0))) {
auto sel = fd.current;
sel /= fd.selected;
out = sel;
res = true;
}
ImGui::SetItemDefaultFocus();
ImGui::SameLine();
if (ImGui::Button("Cancel", ImVec2(width / 2, 0))) {
res = true;
}
ImGui::PopItemWidth();
if (res) {
ImGui::CloseCurrentPopup();
fd.clear();
}
ImGui::EndPopup();
}
return res;
}
bool
save_file_dialog(const char* description,
const char* filters[],
std::filesystem::path& out)
{
if (fd.current.empty()) {
fd.fill_drives();
fd.selected.clear();
std::error_code error;
fd.current = std::filesystem::current_path(error);
std::strcpy(fd.buffer, "file-name.irt");
}
std::filesystem::path next;
bool res = false;
if (ImGui::BeginPopupModal("Select file path to save")) {
bool path_click = false;
fd.show_drives(&path_click, &next);
if (!path_click)
fd.show_path(&path_click, &next);
if (!path_click) {
ImVec2 size = ImGui::GetContentRegionMax();
size.y /= 1.5;
ImGui::BeginChild("##select_files", size);
if (ImGui::Selectable("..##select_file", (fd.selected == ".."))) {
if (next.empty()) {
next = fd.current.parent_path();
fd.selected.clear();
path_click = true;
}
}
for (auto it = fd.paths.begin(), et = fd.paths.end(); it != et;
++it) {
fd.temp.clear();
if (std::filesystem::is_directory(*it))
fmt::format_to(std::back_inserter(fd.temp),
"[Dir] {}",
it->filename().string());
else
fd.temp = it->filename().string();
if (ImGui::Selectable(fd.temp.c_str(),
(it->filename() == fd.selected))) {
fd.selected = it->filename();
if (std::filesystem::is_directory(*it)) {
if (next.empty()) {
fd.selected.clear();
next = fd.current;
next /= it->filename();
path_click = true;
}
}
if (std::filesystem::is_regular_file(*it)) {
strncpy(fd.buffer,
it->filename().string().c_str(),
IM_ARRAYSIZE(fd.buffer));
}
break;
}
}
ImGui::EndChild();
}
if (path_click) {
fd.paths.clear();
static const char* filters[] = { ".irt", nullptr };
fd.extension_filters = filters;
fd.file_filters = nullptr;
fd.copy_files_and_directories(next);
fd.sort();
fd.current = next;
}
ImGui::InputText("File Name", fd.buffer, IM_ARRAYSIZE(fd.buffer));
ImGui::Text("Directory name: %s", fd.current.string().c_str());
float width = ImGui::GetContentRegionAvailWidth();
ImGui::PushItemWidth(width);
if (ImGui::Button("Ok", ImVec2(width / 2, 0))) {
auto sel = fd.current;
sel /= fd.buffer;
out = sel;
res = true;
}
ImGui::SetItemDefaultFocus();
ImGui::SameLine();
if (ImGui::Button("Cancel", ImVec2(width / 2, 0))) {
res = true;
}
ImGui::PopItemWidth();
if (res) {
ImGui::CloseCurrentPopup();
fd.clear();
}
ImGui::EndPopup();
}
return res;
}
} // namespace irt
\ No newline at end of file
......@@ -7,6 +7,9 @@
#include <imgui.h>
#include <filesystem>
#include <string>
namespace irt {
void
......@@ -34,6 +37,18 @@ struct window_logger
void show(bool* is_show);
};
/* Filesytem dialog box */
bool
load_file_dialog(const char* description,
const char* filters[],
std::filesystem::path& out);
bool
save_file_dialog(const char* description,
const char* filters[],
std::filesystem::path& out);
} // namespace irt
#endif
......@@ -2,18 +2,18 @@
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include "gui.hpp"
#include "imnodes.hpp"
#include <filesystem>
#include <future>
#include <mutex>
#include <string>
#include <thread>
#include "imgui.h"
#include "imnodes.hpp"
#include "gui.hpp"
#include <fmt/format.h>
#include <irritator/core.hpp>
#include <irritator/io.hpp>
namespace irt {
......@@ -90,14 +90,14 @@ run_simulation(simulation& sim,
current = begin;
if (auto ret = sim.initialize(current); irt::is_bad(ret)) {
log_w.log(
3, "Simulation initialization failure (%d)", static_cast<int>(ret));
3, "Simulation initialization failure (%d)\n", static_cast<int>(ret));
st = simulation_status::internal_error;
return;
}
do {
if (auto ret = sim.run(current); irt::is_bad(ret)) {
log_w.log(3, "Simulation run failure (%d)", static_cast<int>(ret));
log_w.log(3, "Simulation run failure (%d)\n", static_cast<int>(ret));
st = simulation_status::internal_error;
return;
}
......@@ -111,10 +111,20 @@ run_simulation(simulation& sim,
struct editor
{
small_string<16> name;
std::filesystem::path path;
imnodes::EditorContext* context = nullptr;
bool initialized = false;
bool show = true;
simulation sim;
double simulation_begin = 0.0;
double simulation_end = 10.0;
double simulation_current = 10.0;
std::future<std::tuple<std::string, status>> future_content;
std::thread simulation_thread;
simulation_status st = simulation_status::uninitialized;
bool stop = false;
vector<observation_output> observation_outputs;
void clear()
......@@ -172,15 +182,6 @@ struct editor
return sim.output_ports.get_id(port);
}
simulation sim;
double simulation_begin = 0.0;
double simulation_end = 10.0;
double simulation_current = 10.0;
std::future<std::tuple<std::string, status>> future_content;
std::thread simulation_thread;
simulation_status st = simulation_status::uninitialized;
bool stop = false;
status initialize(u32 id) noexcept
{
if (is_bad(sim.init(1024u, 32768u)) ||
......@@ -707,6 +708,8 @@ struct editor
bool show_editor()
{
imnodes::EditorContextSet(context);
static bool show_load_file_dialog = false;
static bool show_save_file_dialog = false;
ImGuiWindowFlags windows_flags = 0;
windows_flags |= ImGuiWindowFlags_MenuBar;
......@@ -718,9 +721,21 @@ struct editor
if (ImGui::BeginMenuBar()) {
if (ImGui::BeginMenu("File")) {
ImGui::MenuItem("Open");
ImGui::MenuItem("Save");
ImGui::MenuItem("Save as...");
if (ImGui::MenuItem("Open"))
show_load_file_dialog = true;
if (!path.empty() && ImGui::MenuItem("Save")) {
log_w.log(3, "Write into file %s\n", path.string().c_str());
writer w(std::fopen(path.string().c_str(), "w"));
auto ret = w(sim);
if (is_success(ret))
log_w.log(5, "success\n");
else
log_w.log(4, "error writing\n");
}
if (ImGui::MenuItem("Save as..."))
show_save_file_dialog = true;
if (ImGui::MenuItem("Close")) {
ImGui::EndMenu();
......@@ -741,12 +756,12 @@ struct editor
if (ImGui::BeginMenu("Examples")) {
if (ImGui::MenuItem("Insert Lotka Volterra model")) {
if (is_bad(initialize_lotka_volterra()))
log_w.log(3, "Fail to initialize a Lotka Volterra");
log_w.log(3, "Fail to initialize a Lotka Volterra\n");
}
if (ImGui::MenuItem("Insert Izhikevitch model")) {
if (is_bad(initialize_izhikevitch()))
log_w.log(3, "Fail to initialize an Izhikevitch model");
log_w.log(3, "Fail to initialize an Izhikevitch model\n");
}
ImGui::EndMenu();
......@@ -755,6 +770,43 @@ struct editor
ImGui::EndMenuBar();
}
if (show_load_file_dialog) {
static std::string out;
static const char* filters[] = { "irt", nullptr };
ImGui::OpenPopup("Select file path to load");
if (load_file_dialog("Select a file path to load", filters, path)) {
show_load_file_dialog = false;
log_w.log(5, "Load file from %s\n", path.string().c_str());
reader r(std::fopen(path.string().c_str(), "r"));
auto ret = r(sim);
if (is_success(ret))
log_w.log(5, "success\n");
else
log_w.log(4, "error writing\n");
}
}
if (show_save_file_dialog) {
static std::string out;
ImGui::OpenPopup("Select file path to save");
if (save_file_dialog("Select a file path to save", nullptr, path)) {
show_save_file_dialog = false;
log_w.log(5, "Save file to %s\n", path.string().c_str());
log_w.log(3, "Write into file %s\n", path.string().c_str());
writer w(std::fopen(path.string().c_str(), "w"));
auto ret = w(sim);
if (is_success(ret))
log_w.log(5, "success\n");
else
log_w.log(4, "error writing\n");