|
|
|
@ -18,9 +18,9 @@
|
|
|
|
|
|
|
|
|
|
namespace studio {
|
|
|
|
|
|
|
|
|
|
static ox::Vector<studio::Module const*> modules;
|
|
|
|
|
static ox::Vector<Module const*> modules;
|
|
|
|
|
|
|
|
|
|
void registerModule(studio::Module const*mod) noexcept {
|
|
|
|
|
void registerModule(Module const*mod) noexcept {
|
|
|
|
|
if (mod) {
|
|
|
|
|
modules.emplace_back(mod);
|
|
|
|
|
}
|
|
|
|
@ -45,25 +45,25 @@ OX_MODEL_END()
|
|
|
|
|
|
|
|
|
|
StudioUI::StudioUI(turbine::Context &ctx, ox::StringParam projectDataDir) noexcept:
|
|
|
|
|
m_sctx(*this, ctx),
|
|
|
|
|
m_ctx(ctx),
|
|
|
|
|
m_tctx(ctx),
|
|
|
|
|
m_projectDataDir(std::move(projectDataDir)),
|
|
|
|
|
m_projectExplorer(m_ctx),
|
|
|
|
|
m_projectExplorer(m_tctx),
|
|
|
|
|
m_newProject(m_projectDataDir),
|
|
|
|
|
m_aboutPopup(m_ctx) {
|
|
|
|
|
turbine::setApplicationData(m_ctx, &m_sctx);
|
|
|
|
|
m_aboutPopup(m_tctx) {
|
|
|
|
|
turbine::setApplicationData(m_tctx, &m_sctx);
|
|
|
|
|
m_projectExplorer.fileChosen.connect(this, &StudioUI::openFile);
|
|
|
|
|
m_newProject.finished.connect(this, &StudioUI::createOpenProject);
|
|
|
|
|
m_newMenu.finished.connect(this, &StudioUI::openFile);
|
|
|
|
|
ImGui::GetIO().IniFilename = nullptr;
|
|
|
|
|
loadModules();
|
|
|
|
|
// open project and files
|
|
|
|
|
auto const [config, err] = studio::readConfig<StudioConfig>(keelCtx(m_ctx));
|
|
|
|
|
auto const [config, err] = studio::readConfig<StudioConfig>(keelCtx(m_tctx));
|
|
|
|
|
m_showProjectExplorer = config.showProjectExplorer;
|
|
|
|
|
if (!err) {
|
|
|
|
|
auto const openProjErr = openProjectPath(config.projectPath);
|
|
|
|
|
if (!openProjErr) {
|
|
|
|
|
for (auto const&f: config.openFiles) {
|
|
|
|
|
auto openFileErr = openFileActiveTab(f, config.activeTabItemName == f);
|
|
|
|
|
auto const openFileErr = openFileActiveTab(f, config.activeTabItemName == f);
|
|
|
|
|
if (openFileErr) {
|
|
|
|
|
oxErrorf("\nCould not open editor for file:\n\t{}\nReason:\n\t{}\n", f, toStr(openFileErr));
|
|
|
|
|
continue;
|
|
|
|
@ -124,7 +124,7 @@ void StudioUI::draw() noexcept {
|
|
|
|
|
}
|
|
|
|
|
ImGui::End();
|
|
|
|
|
handleKeyInput();
|
|
|
|
|
m_taskRunner.update(m_ctx);
|
|
|
|
|
m_taskRunner.update(m_tctx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void StudioUI::drawMenu() noexcept {
|
|
|
|
@ -143,7 +143,7 @@ void StudioUI::drawMenu() noexcept {
|
|
|
|
|
m_activeEditor->save();
|
|
|
|
|
}
|
|
|
|
|
if (ImGui::MenuItem("Quit", "Ctrl+Q")) {
|
|
|
|
|
turbine::requestShutdown(m_ctx);
|
|
|
|
|
turbine::requestShutdown(m_tctx);
|
|
|
|
|
}
|
|
|
|
|
ImGui::EndMenu();
|
|
|
|
|
}
|
|
|
|
@ -204,10 +204,10 @@ void StudioUI::drawTabs() noexcept {
|
|
|
|
|
if (ImGui::BeginTabItem(e->itemDisplayName().c_str(), &open, flags)) {
|
|
|
|
|
if (m_activeEditor != e.get()) [[unlikely]] {
|
|
|
|
|
m_activeEditor = e.get();
|
|
|
|
|
studio::editConfig<StudioConfig>(keelCtx(m_ctx), [&](StudioConfig &config) {
|
|
|
|
|
studio::editConfig<StudioConfig>(keelCtx(m_tctx), [&](StudioConfig &config) {
|
|
|
|
|
config.activeTabItemName = m_activeEditor->itemPath();
|
|
|
|
|
});
|
|
|
|
|
turbine::setRefreshWithin(m_ctx, 0);
|
|
|
|
|
turbine::setRefreshWithin(m_tctx, 0);
|
|
|
|
|
} else [[likely]] {
|
|
|
|
|
if (m_activeEditorUpdatePending == e.get()) [[unlikely]] {
|
|
|
|
|
m_activeEditorUpdatePending = nullptr;
|
|
|
|
@ -238,13 +238,13 @@ void StudioUI::drawTabs() noexcept {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void StudioUI::loadEditorMaker(studio::EditorMaker const&editorMaker) noexcept {
|
|
|
|
|
void StudioUI::loadEditorMaker(EditorMaker const&editorMaker) noexcept {
|
|
|
|
|
for (auto const&ext : editorMaker.fileTypes) {
|
|
|
|
|
m_editorMakers[ext] = editorMaker.make;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void StudioUI::loadModule(studio::Module const&mod) noexcept {
|
|
|
|
|
void StudioUI::loadModule(Module const&mod) noexcept {
|
|
|
|
|
for (auto const&editorMaker : mod.editors(m_sctx)) {
|
|
|
|
|
loadEditorMaker(editorMaker);
|
|
|
|
|
}
|
|
|
|
@ -261,20 +261,20 @@ void StudioUI::loadModules() noexcept {
|
|
|
|
|
|
|
|
|
|
void StudioUI::toggleProjectExplorer() noexcept {
|
|
|
|
|
m_showProjectExplorer = !m_showProjectExplorer;
|
|
|
|
|
studio::editConfig<StudioConfig>(keelCtx(m_ctx), [&](StudioConfig &config) {
|
|
|
|
|
editConfig<StudioConfig>(keelCtx(m_tctx), [&](StudioConfig &config) {
|
|
|
|
|
config.showProjectExplorer = m_showProjectExplorer;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void StudioUI::redo() noexcept {
|
|
|
|
|
auto undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr;
|
|
|
|
|
auto const undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr;
|
|
|
|
|
if (undoStack && undoStack->canRedo()) {
|
|
|
|
|
oxLogError(m_activeEditor->undoStack()->redo());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void StudioUI::undo() noexcept {
|
|
|
|
|
auto undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr;
|
|
|
|
|
auto const undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr;
|
|
|
|
|
if (undoStack && undoStack->canUndo()) {
|
|
|
|
|
oxLogError(m_activeEditor->undoStack()->undo());
|
|
|
|
|
}
|
|
|
|
@ -293,7 +293,7 @@ void StudioUI::handleKeyInput() noexcept {
|
|
|
|
|
m_activeEditor->copy();
|
|
|
|
|
}
|
|
|
|
|
} else if (ImGui::IsKeyPressed(ImGuiKey_N)) {
|
|
|
|
|
if (turbine::buttonDown(m_ctx, turbine::Key::Mod_Shift)) {
|
|
|
|
|
if (turbine::buttonDown(m_tctx, turbine::Key::Mod_Shift)) {
|
|
|
|
|
m_newProject.open();
|
|
|
|
|
} else {
|
|
|
|
|
m_newMenu.open();
|
|
|
|
@ -303,7 +303,7 @@ void StudioUI::handleKeyInput() noexcept {
|
|
|
|
|
} else if (ImGui::IsKeyPressed(ImGuiKey_S)) {
|
|
|
|
|
save();
|
|
|
|
|
} else if (ImGui::IsKeyPressed(ImGuiKey_Q)) {
|
|
|
|
|
turbine::requestShutdown(m_ctx);
|
|
|
|
|
turbine::requestShutdown(m_tctx);
|
|
|
|
|
} else if (ImGui::IsKeyPressed(ImGuiKey_V)) {
|
|
|
|
|
if (m_activeEditor && m_activeEditor->pasteEnabled()) {
|
|
|
|
|
m_activeEditor->paste();
|
|
|
|
@ -348,18 +348,18 @@ ox::Error StudioUI::createOpenProject(ox::StringViewCR path) noexcept {
|
|
|
|
|
|
|
|
|
|
ox::Error StudioUI::openProjectPath(ox::StringParam path) noexcept {
|
|
|
|
|
OX_REQUIRE_M(fs, keel::loadRomFs(path.view()));
|
|
|
|
|
OX_RETURN_ERROR(keel::setRomFs(keelCtx(m_ctx), std::move(fs)));
|
|
|
|
|
OX_RETURN_ERROR(keel::setRomFs(keelCtx(m_tctx), std::move(fs)));
|
|
|
|
|
OX_RETURN_ERROR(
|
|
|
|
|
ox::make_unique_catch<studio::Project>(keelCtx(m_ctx), std::move(path), m_projectDataDir)
|
|
|
|
|
ox::make_unique_catch<Project>(keelCtx(m_tctx), std::move(path), m_projectDataDir)
|
|
|
|
|
.moveTo(m_project));
|
|
|
|
|
auto const sctx = applicationData<studio::StudioContext>(m_ctx);
|
|
|
|
|
auto const sctx = applicationData<StudioContext>(m_tctx);
|
|
|
|
|
sctx->project = m_project.get();
|
|
|
|
|
turbine::setWindowTitle(m_ctx, ox::sfmt("{} - {}", keelCtx(m_ctx).appName, m_project->projectPath()));
|
|
|
|
|
turbine::setWindowTitle(m_tctx, ox::sfmt("{} - {}", keelCtx(m_tctx).appName, m_project->projectPath()));
|
|
|
|
|
m_project->fileAdded.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel);
|
|
|
|
|
m_project->fileDeleted.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel);
|
|
|
|
|
m_openFiles.clear();
|
|
|
|
|
m_editors.clear();
|
|
|
|
|
studio::editConfig<StudioConfig>(keelCtx(m_ctx), [&](StudioConfig &config) {
|
|
|
|
|
studio::editConfig<StudioConfig>(keelCtx(m_tctx), [&](StudioConfig &config) {
|
|
|
|
|
config.projectPath = ox::String(m_project->projectPath());
|
|
|
|
|
config.openFiles.clear();
|
|
|
|
|
});
|
|
|
|
@ -370,7 +370,7 @@ ox::Error StudioUI::openFile(ox::StringViewCR path) noexcept {
|
|
|
|
|
return openFileActiveTab(path, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ox::Error StudioUI::openFileActiveTab(ox::StringViewCR path, bool makeActiveTab) noexcept {
|
|
|
|
|
ox::Error StudioUI::openFileActiveTab(ox::StringViewCR path, bool const makeActiveTab) noexcept {
|
|
|
|
|
if (!m_project) {
|
|
|
|
|
return ox::Error(1, "No project open to open a file from");
|
|
|
|
|
}
|
|
|
|
@ -384,7 +384,7 @@ ox::Error StudioUI::openFileActiveTab(ox::StringViewCR path, bool makeActiveTab)
|
|
|
|
|
}
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
OX_REQUIRE(ext, studio::fileExt(path));
|
|
|
|
|
OX_REQUIRE(ext, fileExt(path));
|
|
|
|
|
// create Editor
|
|
|
|
|
BaseEditor *editor = nullptr;
|
|
|
|
|
auto const err = m_editorMakers.contains(ext) ?
|
|
|
|
@ -406,7 +406,7 @@ ox::Error StudioUI::openFileActiveTab(ox::StringViewCR path, bool makeActiveTab)
|
|
|
|
|
m_activeEditorUpdatePending = editor;
|
|
|
|
|
}
|
|
|
|
|
// save to config
|
|
|
|
|
studio::editConfig<StudioConfig>(keelCtx(m_ctx), [&path](StudioConfig &config) {
|
|
|
|
|
studio::editConfig<StudioConfig>(keelCtx(m_tctx), [&path](StudioConfig &config) {
|
|
|
|
|
if (!config.openFiles.contains(path)) {
|
|
|
|
|
config.openFiles.emplace_back(path);
|
|
|
|
|
}
|
|
|
|
@ -420,7 +420,7 @@ ox::Error StudioUI::closeFile(ox::StringViewCR path) noexcept {
|
|
|
|
|
}
|
|
|
|
|
std::ignore = m_openFiles.erase(std::remove(m_openFiles.begin(), m_openFiles.end(), path));
|
|
|
|
|
// save to config
|
|
|
|
|
studio::editConfig<StudioConfig>(keelCtx(m_ctx), [&](StudioConfig &config) {
|
|
|
|
|
studio::editConfig<StudioConfig>(keelCtx(m_tctx), [&](StudioConfig &config) {
|
|
|
|
|
std::ignore = config.openFiles.erase(std::remove(config.openFiles.begin(), config.openFiles.end(), path));
|
|
|
|
|
});
|
|
|
|
|
return {};
|
|
|
|
|