Merge commit '71b1e79d61c1530fc6a19201600623e2b0394163'
All checks were successful
Build / build (push) Successful in 1m34s

This commit is contained in:
2025-05-14 22:31:06 -05:00
62 changed files with 240 additions and 997 deletions

View File

@ -12,8 +12,6 @@
namespace keel {
class Context;
class Context {
public:
ox::UPtr<ox::FileSystem> rom;

View File

@ -28,7 +28,7 @@ install(
DIRECTORY
../include/keel
DESTINATION
include/keel
include
)
install(
@ -60,4 +60,11 @@ if(TURBINE_BUILD_TYPE STREQUAL "Native")
OxClaw
OxLogConn
)
install(
TARGETS
KeelPack-AppLib
DESTINATION
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
endif()

View File

@ -182,7 +182,7 @@ ox::Result<ox::CStringView> uuidToPath(Context &ctx, ox::UUID const&uuid) noexce
#endif
}
ox::Error reloadAsset(keel::Context &ctx, ox::StringViewCR assetId) noexcept {
ox::Error reloadAsset(Context &ctx, ox::StringViewCR assetId) noexcept {
if (beginsWith(assetId, "uuid://")) {
return ctx.assetManager.reloadAsset(substr(assetId, 7));
} else {

View File

@ -13,20 +13,9 @@ extern ox::String appVersion;
namespace studio {
AboutPopup::AboutPopup(turbine::Context &ctx) noexcept {
m_text = ox::sfmt("{} - {}", keelCtx(ctx).appName, olympic::appVersion);
}
void AboutPopup::open() noexcept {
m_stage = Stage::Opening;
}
void AboutPopup::close() noexcept {
m_stage = Stage::Closed;
}
bool AboutPopup::isOpen() const noexcept {
return m_stage == Stage::Open;
AboutPopup::AboutPopup(turbine::Context &ctx) noexcept:
Popup("About"),
m_text{sfmt("{} - {}", keelCtx(ctx).appName, olympic::appVersion)} {
}
void AboutPopup::draw(Context &sctx) noexcept {
@ -47,7 +36,7 @@ void AboutPopup::draw(Context &sctx) noexcept {
ig::centerNextWindow(sctx.tctx);
auto open = true;
if (ImGui::BeginPopupModal("About", &open, modalFlags)) {
ImGui::Text("%s", m_text.c_str());
ImGui::Text("%s\n\nBuild date: %s", m_text.c_str(), __DATE__);
ImGui::NewLine();
ImGui::Dummy({148.0f, 0.0f});
ImGui::SameLine();

View File

@ -13,29 +13,14 @@
namespace studio {
class AboutPopup: public studio::Popup {
public:
enum class Stage {
Closed,
Opening,
Open,
};
class AboutPopup final: public ig::Popup {
private:
Stage m_stage = Stage::Closed;
ox::String m_text;
ox::String const m_text;
public:
explicit AboutPopup(turbine::Context &ctx) noexcept;
void open() noexcept override;
void close() noexcept override;
[[nodiscard]]
bool isOpen() const noexcept override;
void draw(studio::Context &sctx) noexcept override;
void draw(Context &sctx) noexcept override;
};

View File

@ -65,10 +65,7 @@ OX_MODEL_END()
StudioUI::StudioUI(turbine::Context &ctx, ox::StringParam projectDataDir) noexcept:
m_sctx{*this, ctx},
m_tctx{ctx},
m_projectDataDir{std::move(projectDataDir)},
m_projectExplorer{keelCtx(m_tctx)},
m_newProject{m_projectDataDir},
m_aboutPopup{m_tctx} {
m_projectDataDir{std::move(projectDataDir)} {
{
ImFontConfig fontCfg;
fontCfg.FontDataOwnedByAtlas = false;
@ -255,7 +252,7 @@ void StudioUI::drawMenu() noexcept {
void StudioUI::drawTabBar() noexcept {
auto const viewport = ImGui::GetContentRegionAvail();
ImGui::BeginChild("TabWindow##MainWindow##Studio", ImVec2(viewport.x, viewport.y));
constexpr auto tabBarFlags = ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_TabListPopupButton;
constexpr auto tabBarFlags = ImGuiTabBarFlags_TabListPopupButton;
if (ImGui::BeginTabBar("TabBar##TabWindow##MainWindow##Studio", tabBarFlags)) {
drawTabs();
ImGui::EndTabBar();
@ -478,6 +475,18 @@ ox::Error StudioUI::handleMoveFile(ox::StringViewCR oldPath, ox::StringViewCR ne
return m_projectExplorer.refreshProjectTreeModel();
}
ox::Error StudioUI::handleDeleteDir(ox::StringViewCR path) noexcept {
auto const p = sfmt("{}/", path);
for (auto &e : m_editors) {
if (beginsWith(e->itemPath(), p)) {
oxLogError(closeFile(path));
m_closeActiveTab = true;
break;
}
}
return m_projectExplorer.refreshProjectTreeModel();
}
ox::Error StudioUI::handleDeleteFile(ox::StringViewCR path) noexcept {
for (auto &e : m_editors) {
if (path == e->itemPath()) {
@ -523,6 +532,7 @@ ox::Error StudioUI::openProjectPath(ox::StringParam path) noexcept {
m_newDirDialog.newDir.connect(m_sctx.project, &Project::mkdir);
m_project->dirAdded.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel);
m_project->fileAdded.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel);
m_project->dirDeleted.connect(this, &StudioUI::handleDeleteDir);
m_project->fileDeleted.connect(this, &StudioUI::handleDeleteFile);
m_project->fileMoved.connect(this, &StudioUI::handleMoveFile);
m_openFiles.clear();

View File

@ -36,7 +36,7 @@ class StudioUI: public ox::SignalHandler {
TaskRunner m_taskRunner;
ox::Vector<ox::UPtr<BaseEditor>> m_editors;
ox::HashMap<ox::String, EditorMaker::Func> m_editorMakers;
ProjectExplorer m_projectExplorer;
ProjectExplorer m_projectExplorer{keelCtx(m_tctx)};
ox::Vector<ox::String> m_openFiles;
BaseEditor *m_activeEditorOnLastDraw = nullptr;
BaseEditor *m_activeEditor = nullptr;
@ -45,6 +45,7 @@ class StudioUI: public ox::SignalHandler {
ox::Vector<ox::Pair<ox::String>> m_queuedMoves;
ox::Vector<ox::Pair<ox::String>> m_queuedDirMoves;
NewMenu m_newMenu{keelCtx(m_tctx)};
AboutPopup m_aboutPopup{m_tctx};
DeleteConfirmation m_deleteConfirmation;
NewDir m_newDirDialog;
ig::QuestionPopup m_closeFileConfirm{"Close File?", "This file has unsaved changes. Close?"};
@ -55,8 +56,7 @@ class StudioUI: public ox::SignalHandler {
ig::MessagePopup m_messagePopup{"Message", ""};
MakeCopyPopup m_copyFilePopup;
RenameFile m_renameFile;
NewProject m_newProject;
AboutPopup m_aboutPopup;
NewProject m_newProject{m_projectDataDir};
ox::Array<Widget*, 10> const m_widgets {
&m_closeFileConfirm,
&m_closeAppConfirm,
@ -126,6 +126,8 @@ class StudioUI: public ox::SignalHandler {
ox::Error handleMoveFile(ox::StringViewCR oldPath, ox::StringViewCR newPath, ox::UUID const&id) noexcept;
ox::Error handleDeleteDir(ox::StringViewCR path) noexcept;
ox::Error handleDeleteFile(ox::StringViewCR path) noexcept;
ox::Error createOpenProject(ox::StringViewCR path) noexcept;

View File

@ -23,6 +23,7 @@ enum class ProjectEvent {
// FileRecognized is triggered for all matching files upon a new
// subscription to a section of the project and upon the addition of a file.
FileRecognized,
DirDeleted,
FileDeleted,
FileUpdated,
FileMoved,
@ -134,6 +135,7 @@ class Project: public ox::SignalHandler {
// file.
ox::Signal<ox::Error(ox::StringViewCR)> fileRecognized;
ox::Signal<ox::Error(ox::StringViewCR)> fileDeleted;
ox::Signal<ox::Error(ox::StringViewCR)> dirDeleted;
ox::Signal<ox::Error(ox::StringViewCR path, ox::UUID const&id)> fileUpdated;
ox::Signal<ox::Error(ox::StringViewCR oldPath, ox::StringViewCR newPath, ox::UUID const&id)> fileMoved;
@ -201,6 +203,9 @@ ox::Error Project::subscribe(ProjectEvent e, ox::SignalHandler *tgt, Functor &&s
connect(this, &Project::fileRecognized, tgt, slot);
break;
}
case ProjectEvent::DirDeleted:
connect(this, &Project::dirDeleted, tgt, slot);
break;
case ProjectEvent::FileDeleted:
connect(this, &Project::fileDeleted, tgt, slot);
break;

View File

@ -44,5 +44,5 @@ install(
DIRECTORY
../include/studio
DESTINATION
include/studio
include
)

View File

@ -93,6 +93,9 @@ void FileTreeModel::draw(turbine::Context &tctx) const noexcept {
if (ImGui::IsItemActivated() || ImGui::IsItemClicked(1)) {
m_explorer.setSelectedNode(this);
}
if (ImGui::IsItemFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete)) {
m_explorer.fileDeleted(m_fullPath);
}
ig::IDStackItem const idStackItem{m_name};
m_explorer.drawDirContextMenu(m_fullPath);
if (m_explorer.fileDraggable()) {

View File

@ -145,7 +145,7 @@ ox::Error Project::deleteItem(ox::StringViewCR path) noexcept {
}
auto const err = m_fs.remove(path);
if (!err) {
fileDeleted.emit(path);
dirDeleted.emit(path);
}
return err;
} else {

View File

@ -38,7 +38,7 @@ install(
DIRECTORY
../include/turbine
DESTINATION
include/turbine
include
)
install(