[studio] Add Back/Forward navigation
All checks were successful
Build / build (push) Successful in 1m14s
All checks were successful
Build / build (push) Successful in 1m14s
This commit is contained in:
@@ -34,18 +34,6 @@ static bool shutdownHandler(turbine::Context &ctx) {
|
|||||||
return sctx->ui.handleShutdown();
|
return sctx->ui.handleShutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
void navigateTo(Context &ctx, ox::StringParam filePath, ox::StringParam navArgs) noexcept {
|
|
||||||
ox::String path = std::move(filePath);
|
|
||||||
if (beginsWith(path, "uuid://")) {
|
|
||||||
auto [p, err] = keel::uuidUrlToPath(keelCtx(ctx), path);
|
|
||||||
if (err) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
path = p;
|
|
||||||
}
|
|
||||||
ctx.ui.navigateTo(std::move(path), std::move(navArgs));
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace ig {
|
namespace ig {
|
||||||
extern bool s_mainWinHasFocus;
|
extern bool s_mainWinHasFocus;
|
||||||
}
|
}
|
||||||
@@ -147,6 +135,9 @@ StudioUI::StudioUI(turbine::Context &tctx, ox::StringParam projectDataDir) noexc
|
|||||||
oxLogError(headerizeConfigFile<StudioConfigV1>(kctx));
|
oxLogError(headerizeConfigFile<StudioConfigV1>(kctx));
|
||||||
turbine::setApplicationData(m_tctx, &m_sctx);
|
turbine::setApplicationData(m_tctx, &m_sctx);
|
||||||
turbine::setShutdownHandler(m_tctx, shutdownHandler);
|
turbine::setShutdownHandler(m_tctx, shutdownHandler);
|
||||||
|
m_sctx.navCallback = [this](ox::StringParam filePath, ox::StringParam navArgs) {
|
||||||
|
handleNavigationChange(std::move(filePath), std::move(navArgs));
|
||||||
|
};
|
||||||
m_projectExplorer.fileChosen.connect(this, &StudioUI::openFile);
|
m_projectExplorer.fileChosen.connect(this, &StudioUI::openFile);
|
||||||
m_projectExplorer.addDir.connect(this, &StudioUI::addDir);
|
m_projectExplorer.addDir.connect(this, &StudioUI::addDir);
|
||||||
m_projectExplorer.addItem.connect(this, &StudioUI::addFile);
|
m_projectExplorer.addItem.connect(this, &StudioUI::addFile);
|
||||||
@@ -186,7 +177,7 @@ void StudioUI::handleKeyEvent(turbine::Key const key, bool const down) noexcept
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void StudioUI::navigateTo(ox::StringParam path, ox::StringParam navArgs) noexcept {
|
void StudioUI::handleNavigationChange(ox::StringParam path, ox::StringParam navArgs) noexcept {
|
||||||
m_navAction.emplace(std::move(path), std::move(navArgs));
|
m_navAction.emplace(std::move(path), std::move(navArgs));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,7 +261,7 @@ void StudioUI::drawMenu() noexcept {
|
|||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
if (ImGui::BeginMenu("Edit")) {
|
if (ImGui::BeginMenu("Edit")) {
|
||||||
auto undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr;
|
auto const undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr;
|
||||||
if (ImGui::MenuItem(
|
if (ImGui::MenuItem(
|
||||||
"Undo", STUDIO_CTRL "+Z", false, undoStack && undoStack->canUndo())) {
|
"Undo", STUDIO_CTRL "+Z", false, undoStack && undoStack->canUndo())) {
|
||||||
oxLogError(undoStack->undo());
|
oxLogError(undoStack->undo());
|
||||||
@@ -310,6 +301,17 @@ void StudioUI::drawMenu() noexcept {
|
|||||||
}
|
}
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
|
if (ImGui::BeginMenu("Navigate")) {
|
||||||
|
if (ImGui::MenuItem("Back", STUDIO_CTRL "+{", false, m_sctx.navIdx > 1)) {
|
||||||
|
navigateBack(m_sctx);
|
||||||
|
}
|
||||||
|
if (ImGui::MenuItem(
|
||||||
|
"Forward", STUDIO_CTRL "+}", false,
|
||||||
|
m_sctx.navIdx < m_sctx.navStack.size())) {
|
||||||
|
navigateForward(m_sctx);
|
||||||
|
}
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
if (ImGui::BeginMenu("Help")) {
|
if (ImGui::BeginMenu("Help")) {
|
||||||
if (ImGui::MenuItem("About")) {
|
if (ImGui::MenuItem("About")) {
|
||||||
m_aboutPopup.open();
|
m_aboutPopup.open();
|
||||||
@@ -353,6 +355,13 @@ void StudioUI::drawTabs() noexcept {
|
|||||||
}
|
}
|
||||||
if (m_activeEditorOnLastDraw != e.get()) [[unlikely]] {
|
if (m_activeEditorOnLastDraw != e.get()) [[unlikely]] {
|
||||||
m_activeEditor->onActivated();
|
m_activeEditor->onActivated();
|
||||||
|
if (!m_sctx.navIdx ||
|
||||||
|
(m_sctx.navIdx <= m_sctx.navStack.size() &&
|
||||||
|
m_sctx.navStack[m_sctx.navIdx - 1].filePath != m_activeEditor->itemPath())) {
|
||||||
|
m_sctx.navStack.resize(m_sctx.navIdx);
|
||||||
|
++m_sctx.navIdx;
|
||||||
|
m_sctx.navStack.emplace_back(ox::String{m_activeEditor->itemPath()}, ox::String{""});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (m_closeActiveTab) [[unlikely]] {
|
if (m_closeActiveTab) [[unlikely]] {
|
||||||
ImGui::SetTabItemClosed(e->itemDisplayName().c_str());
|
ImGui::SetTabItemClosed(e->itemDisplayName().c_str());
|
||||||
@@ -395,9 +404,14 @@ void StudioUI::drawTabs() noexcept {
|
|||||||
m_closeActiveTab = false;
|
m_closeActiveTab = false;
|
||||||
}
|
}
|
||||||
if (m_navAction) {
|
if (m_navAction) {
|
||||||
oxLogError(openFile(m_navAction->path));
|
if (!openFile(m_navAction->path)) {
|
||||||
m_activeEditor->navigateTo(m_navAction->args);
|
m_activeEditor->navigateTo(m_navAction->args);
|
||||||
m_navAction.reset();
|
m_navAction.reset();
|
||||||
|
} else {
|
||||||
|
//auto const i = m_sctx.navIdx - 1;
|
||||||
|
//oxDebugf("deleting {}", m_sctx.navStack[i].filePath);
|
||||||
|
//std::ignore = m_sctx.navStack.erase(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -506,6 +520,22 @@ void StudioUI::handleKeyInput() noexcept {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if constexpr (ox::defines::OS == ox::OS::Darwin) {
|
||||||
|
if (ImGui::IsKeyPressed(ImGuiKey_LeftBracket)) {
|
||||||
|
navigateBack(m_sctx);
|
||||||
|
} else if (ImGui::IsKeyPressed(ImGuiKey_RightBracket)) {
|
||||||
|
navigateForward(m_sctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if constexpr (ox::defines::OS != ox::OS::Darwin) {
|
||||||
|
if (ImGui::IsKeyDown(ImGuiKey_ModAlt)) {
|
||||||
|
if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow)) {
|
||||||
|
navigateBack(m_sctx);
|
||||||
|
} else if (ImGui::IsKeyPressed(ImGuiKey_RightArrow)) {
|
||||||
|
navigateForward(m_sctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -664,7 +694,10 @@ ox::Error StudioUI::openFile(ox::StringViewCR path) noexcept {
|
|||||||
|
|
||||||
ox::Error StudioUI::openFileActiveTab(ox::StringViewCR path, bool const makeActiveTab) noexcept {
|
ox::Error StudioUI::openFileActiveTab(ox::StringViewCR path, bool const makeActiveTab) noexcept {
|
||||||
if (!m_project) {
|
if (!m_project) {
|
||||||
return ox::Error(1, "No project open to open a file from");
|
return ox::Error(1, "no project open to open a file from");
|
||||||
|
}
|
||||||
|
if (!m_project->romFs().exists(path)) {
|
||||||
|
return ox::Error{1, "file does note exist"};
|
||||||
}
|
}
|
||||||
if (m_openFiles.contains(path)) {
|
if (m_openFiles.contains(path)) {
|
||||||
for (auto &e : m_editors) {
|
for (auto &e : m_editors) {
|
||||||
|
@@ -87,7 +87,7 @@ class StudioUI final: public ox::SignalHandler {
|
|||||||
|
|
||||||
void handleKeyEvent(turbine::Key, bool down) noexcept;
|
void handleKeyEvent(turbine::Key, bool down) noexcept;
|
||||||
|
|
||||||
void navigateTo(ox::StringParam path, ox::StringParam navArgs) noexcept;
|
void handleNavigationChange(ox::StringParam path, ox::StringParam navArgs) noexcept;
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
constexpr Project *project() noexcept {
|
constexpr Project *project() noexcept {
|
||||||
|
@@ -16,11 +16,21 @@ class StudioUI;
|
|||||||
|
|
||||||
struct Context {
|
struct Context {
|
||||||
public:
|
public:
|
||||||
|
friend void navigateTo(Context &ctx, ox::StringParam filePath, ox::StringParam navArgs) noexcept;
|
||||||
|
friend void navigateBack(Context &ctx) noexcept;
|
||||||
|
friend void navigateForward(Context &ctx) noexcept;
|
||||||
friend StudioUI;
|
friend StudioUI;
|
||||||
StudioUI &ui;
|
StudioUI &ui;
|
||||||
Project *project = nullptr;
|
Project *project = nullptr;
|
||||||
turbine::Context &tctx;
|
turbine::Context &tctx;
|
||||||
protected:
|
protected:
|
||||||
|
struct NavPath {
|
||||||
|
ox::String filePath;
|
||||||
|
ox::String navArgs;
|
||||||
|
};
|
||||||
|
size_t navIdx{};
|
||||||
|
ox::Vector<NavPath> navStack;
|
||||||
|
std::function<void(ox::StringParam filePath, ox::StringParam navArgs)> navCallback;
|
||||||
Context(StudioUI &pUi, turbine::Context &pTctx) noexcept:
|
Context(StudioUI &pUi, turbine::Context &pTctx) noexcept:
|
||||||
ui{pUi}, tctx{pTctx} {}
|
ui{pUi}, tctx{pTctx} {}
|
||||||
};
|
};
|
||||||
@@ -37,4 +47,8 @@ inline keel::Context const &keelCtx(Context const &ctx) noexcept {
|
|||||||
|
|
||||||
void navigateTo(Context &ctx, ox::StringParam filePath, ox::StringParam navArgs = "") noexcept;
|
void navigateTo(Context &ctx, ox::StringParam filePath, ox::StringParam navArgs = "") noexcept;
|
||||||
|
|
||||||
|
void navigateBack(Context &ctx) noexcept;
|
||||||
|
|
||||||
|
void navigateForward(Context &ctx) noexcept;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -348,6 +348,8 @@ class QuestionPopup: public Popup {
|
|||||||
|
|
||||||
QuestionPopup(ox::StringParam title, ox::StringParam question) noexcept;
|
QuestionPopup(ox::StringParam title, ox::StringParam question) noexcept;
|
||||||
|
|
||||||
|
void setQuestion(ox::StringParam msg) noexcept;
|
||||||
|
|
||||||
void draw(Context &ctx) noexcept override;
|
void draw(Context &ctx) noexcept override;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
add_library(
|
add_library(
|
||||||
Studio
|
Studio
|
||||||
configio.cpp
|
configio.cpp
|
||||||
|
context.cpp
|
||||||
editor.cpp
|
editor.cpp
|
||||||
filepickerpopup.cpp
|
filepickerpopup.cpp
|
||||||
filetreemodel.cpp
|
filetreemodel.cpp
|
||||||
|
69
src/olympic/studio/modlib/src/context.cpp
Normal file
69
src/olympic/studio/modlib/src/context.cpp
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <studio/context.hpp>
|
||||||
|
|
||||||
|
namespace studio {
|
||||||
|
|
||||||
|
void navigateTo(Context &ctx, ox::StringParam filePath, ox::StringParam navArgs) noexcept {
|
||||||
|
ox::String path = std::move(filePath);
|
||||||
|
if (beginsWith(path, "uuid://")) {
|
||||||
|
auto const [p, err] = keel::uuidUrlToPath(keelCtx(ctx), path);
|
||||||
|
if (err) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
path = p;
|
||||||
|
}
|
||||||
|
ctx.navStack.resize(ctx.navIdx + 1);
|
||||||
|
ctx.navStack.emplace_back(ox::String{path}, ox::String{navArgs.view()});
|
||||||
|
try {
|
||||||
|
ctx.navCallback(std::move(path), std::move(navArgs));
|
||||||
|
} catch (std::exception const &e) {
|
||||||
|
oxErrf("Nav error: {}", e.what());
|
||||||
|
oxAssert(false, "Nav error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void navigateBack(Context &ctx) noexcept {
|
||||||
|
if (!ctx.navIdx) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
--ctx.navIdx;
|
||||||
|
while (ctx.navIdx < ctx.navStack.size() && ctx.navIdx) {
|
||||||
|
auto const i = ctx.navIdx - 1;
|
||||||
|
auto const &n = ctx.navStack[i];
|
||||||
|
if (!ctx.project->exists(n.filePath)) {
|
||||||
|
std::ignore = ctx.navStack.erase(i);
|
||||||
|
--ctx.navIdx;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
ctx.navCallback(n.filePath, n.navArgs);
|
||||||
|
} catch (std::exception const &e) {
|
||||||
|
oxAssert(ctx.navCallback != nullptr, "navCallback is null");
|
||||||
|
oxErrf("navigateForward failed: {}", e.what());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void navigateForward(Context &ctx) noexcept {
|
||||||
|
while (ctx.navIdx < ctx.navStack.size()) {
|
||||||
|
auto const &n = ctx.navStack[ctx.navIdx];
|
||||||
|
try {
|
||||||
|
ctx.navCallback(n.filePath, n.navArgs);
|
||||||
|
} catch (std::exception const &e) {
|
||||||
|
oxAssert(ctx.navCallback != nullptr, "navCallback is null");
|
||||||
|
oxErrf("navigateForward failed: {}", e.what());
|
||||||
|
}
|
||||||
|
if (!ctx.project->exists(n.filePath)) {
|
||||||
|
std::ignore = ctx.navStack.erase(ctx.navIdx);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
++ctx.navIdx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -267,6 +267,10 @@ QuestionPopup::QuestionPopup(ox::StringParam title, ox::StringParam question) no
|
|||||||
m_question{std::move(question)} {
|
m_question{std::move(question)} {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QuestionPopup::setQuestion(ox::StringParam msg) noexcept {
|
||||||
|
m_question = std::move(msg);
|
||||||
|
}
|
||||||
|
|
||||||
void QuestionPopup::draw(Context &ctx) noexcept {
|
void QuestionPopup::draw(Context &ctx) noexcept {
|
||||||
switch (m_stage) {
|
switch (m_stage) {
|
||||||
case Stage::Closed:
|
case Stage::Closed:
|
||||||
|
Reference in New Issue
Block a user