[nostalgia] Update Studio to handle tabs and open directory dialog on Mac, Update core::init

This commit is contained in:
2021-10-31 13:31:12 -05:00
parent e29f65f351
commit ad743565b2
28 changed files with 416 additions and 155 deletions
+144 -50
View File
@@ -11,6 +11,7 @@
#include <nostalgia/core/core.hpp>
#include "lib/configio.hpp"
#include "builtinmodules.hpp"
#include "filedialogmanager.hpp"
#include "studioapp.hpp"
@@ -20,58 +21,62 @@ struct StudioConfig {
static constexpr auto TypeName = "net.drinkingtea.nostalgia.studio.StudioConfig";
static constexpr auto TypeVersion = 1;
ox::String projectPath;
ox::Vector<ox::String> openFiles;
};
template<typename T>
constexpr ox::Error model(T *h, StudioConfig *config) noexcept {
h->template setTypeInfo<StudioConfig>();
oxReturnError(h->field("project_path", &config->projectPath));
oxReturnError(h->field("open_files", &config->openFiles));
return OxError(0);
}
StudioUI::StudioUI() noexcept {
m_widgets.push_back(&m_projectExplorer);
if (const auto [config, err] = studio::readConfig<StudioConfig>(); !err) {
StudioUI::StudioUI(core::Context *ctx) noexcept {
m_ctx = ctx;
m_projectExplorer = new ProjectExplorer(m_ctx);
m_widgets.emplace_back(m_projectExplorer);
m_projectExplorer->fileChosen.connect(this, &StudioUI::openFile);
loadModules();
// open project and files
const auto [config, err] = studio::readConfig<StudioConfig>(ctx);
if (!err) {
oxIgnoreError(openProject(config.projectPath));
for (const auto &f : config.openFiles) {
oxLogError(openFile(f));
}
} else {
oxErrf("Could not open studio config file: {}\n", err.msg);
}
}
static ox::Result<ox::UniquePtr<ProjectTreeModel>>
buildProjectTreeModel(const ox::String &name, const ox::String &path, ox::FileSystem *fs) noexcept {
ox::Vector<ox::UniquePtr<ProjectTreeModel>> outChildren;
oxRequire(stat, fs->stat(path.c_str()));
if (stat.fileType == ox::FileType::Directory) {
oxRequireM(children, fs->ls(path));
//std::sort(children.begin(), children.end());
for (const auto &childName : children) {
if (childName[0] != '.') {
const auto childPath = ox::sfmt("{}/{}", path, childName);
oxRequireM(child, buildProjectTreeModel(childName, childPath, fs));
outChildren.emplace_back(std::move(child));
}
if (err.msg) {
oxErrf("Could not open studio config file: {}\n", err.msg);
} else {
oxErrf("Could not open studio config file: {} ({}:{})\n", err.errCode, err.file, err.line);
}
}
return ox::make_unique<ProjectTreeModel>(name, std::move(outChildren));
}
void StudioUI::update(core::Context *ctx) noexcept {
m_taskRunner.update(ctx);
void StudioUI::update() noexcept {
m_taskRunner.update(m_ctx);
}
void StudioUI::draw(core::Context *ctx) noexcept {
drawMenu(ctx);
drawToolbar(ctx);
void StudioUI::draw() noexcept {
drawMenu();
drawToolbar();
drawTabs();
for (auto &w : m_widgets) {
w->draw(ctx);
w->draw(m_ctx);
}
constexpr auto aboutFlags = ImGuiWindowFlags_NoCollapse
| ImGuiWindowFlags_NoResize
| ImGuiWindowFlags_Modal;
if (m_aboutEnabled &&
ImGui::Begin("About", &m_aboutEnabled, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize)) {
ImGui::Begin("About", &m_aboutEnabled, aboutFlags)) {
ImGui::SetWindowSize(ImVec2(400, 250));
ImGui::Text("Nostalgia Studio - dev build");
ImGui::End();
}
}
void StudioUI::drawMenu(core::Context *ctx) noexcept {
void StudioUI::drawMenu() noexcept {
if (ImGui::BeginMainMenuBar()) {
if (ImGui::BeginMenu("File")) {
if (ImGui::MenuItem("New...", "Ctrl+N")) {
@@ -82,7 +87,7 @@ void StudioUI::drawMenu(core::Context *ctx) noexcept {
if (ImGui::MenuItem("Save", "Ctrl+S", false, m_saveEnabled)) {
}
if (ImGui::MenuItem("Quit", "Ctrl+Q")) {
core::shutdown(ctx);
core::shutdown(m_ctx);
}
ImGui::EndMenu();
}
@@ -99,45 +104,134 @@ void StudioUI::drawMenu(core::Context *ctx) noexcept {
}
}
void StudioUI::drawToolbar(core::Context*) noexcept {
void StudioUI::drawToolbar() noexcept {
constexpr auto BtnWidth = 96;
constexpr auto BtnHeight = 32;
const auto viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(ImVec2(viewport->Pos.x, viewport->Pos.y + 20));
ImGui::SetNextWindowSize(ImVec2(viewport->Size.x, 48));
ImGui::SetNextWindowPos(ImVec2(viewport->Pos.x + 1, viewport->Pos.y + 21));
ImGui::SetNextWindowSize(ImVec2(viewport->Size.x - 1, 48));
const auto flags = ImGuiWindowFlags_NoTitleBar
| ImGuiWindowFlags_NoResize
| ImGuiWindowFlags_NoMove
| ImGuiWindowFlags_NoScrollbar
| ImGuiWindowFlags_NoSavedSettings;
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0);
ImGui::Begin("MainToolbar", nullptr, flags);
ImGui::PopStyleVar();
ImGui::Begin("MainToolbar##MainWindow", nullptr, flags);
{
ImGui::Button("New##MainToolbar", ImVec2(BtnWidth, BtnHeight));
ImGui::Button("New##MainToolbar##MainWindow", ImVec2(BtnWidth, BtnHeight));
ImGui::SameLine();
if (ImGui::Button("Open Project##MainToolbar", ImVec2(BtnWidth, BtnHeight))) {
if (ImGui::Button("Open Project##MainToolbar##MainWindow", ImVec2(BtnWidth, BtnHeight))) {
m_taskRunner.add(new FileDialogManager(this, &StudioUI::openProject));
}
ImGui::SameLine();
ImGui::Button("Save##MainToolbar", ImVec2(BtnWidth, BtnHeight));
ImGui::Button("Save##MainToolbar##MainWindow", ImVec2(BtnWidth, BtnHeight));
}
ImGui::End();
}
ox::Error StudioUI::openProject(const ox::String &path) noexcept {
m_project = ox::make_unique<studio::Project>(path);
m_project->fileAdded.connect(this, &StudioUI::refreshProjectTreeModel);
m_project->fileDeleted.connect(this, &StudioUI::refreshProjectTreeModel);
oxReturnError(studio::editConfig<StudioConfig>([path](StudioConfig *config) {
config->projectPath = path;
}));
return refreshProjectTreeModel();
void StudioUI::drawTabs() noexcept {
const auto viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(ImVec2(viewport->Pos.x + 316, viewport->Pos.y + 71));
ImGui::SetNextWindowSize(ImVec2(viewport->Size.x - 316, viewport->Size.y - 71));
constexpr auto windowFlags = ImGuiWindowFlags_NoTitleBar
| ImGuiWindowFlags_NoResize
| ImGuiWindowFlags_NoMove
| ImGuiWindowFlags_NoScrollbar
| ImGuiWindowFlags_NoSavedSettings;
ImGui::Begin("TabWindow##MainWindow", nullptr, windowFlags);
constexpr auto tabBarFlags = ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_TabListPopupButton;
if (ImGui::BeginTabBar("TabBar##TabWindow##MainWindow", tabBarFlags)) {
for (auto &e : m_editors) {
bool open = true;
if (ImGui::BeginTabItem(e->itemDisplayName().c_str(), &open)) {
e->draw(m_ctx);
ImGui::EndTabItem();
}
if (!open) {
e->close();
try {
oxThrowError(m_editors.erase(e));
} catch (const std::exception &e) {
oxErrf("Editor tab deletion failed: {}", e.what());
}
}
}
ImGui::EndTabBar();
}
ImGui::End();
}
ox::Error StudioUI::refreshProjectTreeModel(const ox::String&) noexcept {
oxRequireM(model, buildProjectTreeModel("Project", "/", m_project->romFs()));
m_projectExplorer.setModel(std::move(model));
void StudioUI::loadEditorMaker(const studio::EditorMaker &editorMaker) noexcept {
for (auto &ext : editorMaker.fileTypes) {
m_editorMakers[ext] = editorMaker.make;
}
}
void StudioUI::loadModule(studio::Module *module) noexcept {
for (auto &editorMaker : module->editors(m_ctx)) {
loadEditorMaker(editorMaker);
}
}
void StudioUI::loadModules() noexcept {
//for (auto &moduleMaker : BuiltinModules) {
// const auto module = moduleMaker();
// loadModule(module.get());
//}
}
ox::Error StudioUI::openProject(const ox::String &path) noexcept {
oxRequireM(fs, core::loadRomFs(path.c_str()));
m_ctx->rom = std::move(fs);
core::setWindowTitle(m_ctx, ox::sfmt("Nostalgia Studio - {}", path).c_str());
m_project = ox::make_unique<studio::Project>(m_ctx->rom.get(), path);
m_project->fileAdded.connect(m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel);
m_project->fileDeleted.connect(m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel);
studio::editConfig<StudioConfig>(m_ctx, [&](StudioConfig *config) {
config->projectPath = path;
});
return m_projectExplorer->refreshProjectTreeModel();
}
ox::Error StudioUI::openFile(const ox::String &path) noexcept {
if (m_openFiles.contains(path)) {
return OxError(0);
}
// find file extension
const auto extStart = std::find(path.crbegin(), path.crend(), '.').offset();
if (!extStart) {
return OxError(1, "Cannot open a file without valid extension.");
}
const auto ext = path.substr(extStart + 1);
// create Editor
if (!m_editorMakers.contains(ext)) {
return OxError(1, "There is no editor for this file extension");
}
try {
auto editor = m_editorMakers[ext](path);
editor->closed.connect(this, &StudioUI::closeFile);
m_editors.emplace_back(editor);
} catch (const ox::Exception &ex) {
return ex.toError();
}
m_openFiles.emplace_back(path);
// save to config
studio::editConfig<StudioConfig>(m_ctx, [&](StudioConfig *config) {
if (!config->openFiles.contains((path))) {
config->openFiles.emplace_back(path);
}
});
return OxError(0);
}
ox::Error StudioUI::closeFile(const ox::String &path) noexcept {
if (!m_openFiles.contains(path)) {
return OxError(0);
}
oxIgnoreError(m_openFiles.erase(std::remove(m_openFiles.begin(), m_openFiles.end(), path)));
// save to config
studio::editConfig<StudioConfig>(m_ctx, [&](StudioConfig *config) {
oxIgnoreError(config->openFiles.erase(std::remove(config->openFiles.begin(), config->openFiles.end(), path)));
});
return OxError(0);
}