Squashed 'deps/nostalgia/' changes from e78c4050..857587c1

857587c1 [studio] Cleanup
eb3d53c9 [studio] Cleanup
14d58f3f [studio] Fix Navigation shortcuts for non-Mac systems
5f239790 [studio,nostalgia/gfx/studio/tilesheet] Fix copy/cut/paste enablement when there is no selection
58e0ecb4 [studio] Make FilePickerPopup accept on double click of a file
8838bf42 [studio] Fix to properly copy file that has the same name as deleted file
bddc544d [nostalgia] Update release notes
a9437191 [studio,turbine] Add support for mouse back/forward buttons
9d8da7cc [ox/std] Make strToInt return error for empty string
394b568e [studio] Add Back/Forward navigation
78e9f70d [nostalgia] Update release notes
12e5623f [ox/logconn] Add exception handling for logger thread
cfdfb0a8 [studio] Fix file deletion to close file even if not active
56e66530 [studio] Cleanup
7415ce4b [nostalgia/gfx/studio] Cleanup
05f42150 [olympic] Add new loc command to Makefile
8ea2bc69 [nostalgia] Update release notes
c7809241 [studio] Add [DEBUG] tag to About in debug builds
8c538560 [nostalgia/gfx/studio/palette] Make RGB key shortcuts work when color channel inputs are focused
c3e75bdb [nostalgia/gfx/studio/tilesheet] Cleanup

git-subtree-dir: deps/nostalgia
git-subtree-split: 857587c18b4695eacd31457e3c30b4971b4e46e8
This commit is contained in:
2025-06-21 08:48:13 -05:00
parent 7688c05bac
commit 7371df4295
38 changed files with 528 additions and 216 deletions

View File

@@ -15,11 +15,24 @@ namespace studio {
class StudioUI;
struct Context {
StudioUI &ui;
Project *project = nullptr;
turbine::Context &tctx;
Context(StudioUI &pUi, turbine::Context &pTctx) noexcept:
ui(pUi), tctx(pTctx) {}
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;
StudioUI &ui;
Project *project = nullptr;
turbine::Context &tctx;
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:
ui{pUi}, tctx{pTctx} {}
};
[[nodiscard]]
@@ -27,6 +40,15 @@ inline keel::Context &keelCtx(Context &ctx) noexcept {
return keelCtx(ctx.tctx);
}
[[nodiscard]]
inline keel::Context const &keelCtx(Context const &ctx) noexcept {
return keelCtx(ctx.tctx);
}
void navigateTo(Context &ctx, ox::StringParam filePath, ox::StringParam navArgs = "") noexcept;
void navigateBack(Context &ctx) noexcept;
void navigateForward(Context &ctx) noexcept;
}

View File

@@ -13,7 +13,11 @@ class FilePickerPopup {
private:
ox::String m_name;
FileExplorer m_explorer;
struct Explorer: public FileExplorer {
mutable bool opened{};
explicit Explorer(keel::Context &kctx);
void fileOpened(ox::StringViewCR path) const noexcept override;
} m_explorer;
ox::Vector<ox::String> const m_fileExts;
bool m_open{};
@@ -33,6 +37,10 @@ class FilePickerPopup {
ox::Optional<ox::String> draw(Context &ctx) noexcept;
private:
[[nodiscard]]
ox::Optional<ox::String> handlePick() noexcept;
};
}

View File

@@ -46,14 +46,14 @@ ox::Result<T> getDragDropPayload() noexcept {
static_cast<size_t>(payload->DataSize)});
}
ox::Error setDragDropPayload(ox::CStringViewCR name, auto const&obj) noexcept {
ox::Error setDragDropPayload(ox::CStringViewCR name, auto const &obj) noexcept {
OX_REQUIRE(buff, ox::writeClaw(obj, ox::ClawFormat::Metal));
ImGui::SetDragDropPayload(name.c_str(), buff.data(), buff.size());
return {};
}
template<typename T>
ox::Error setDragDropPayload(T const&obj) noexcept {
ox::Error setDragDropPayload(T const &obj) noexcept {
OX_REQUIRE(buff, ox::writeClaw(obj, ox::ClawFormat::Metal));
ImGui::SetDragDropPayload(ox::ModelTypeName_v<T>, buff.data(), buff.size());
return {};
@@ -77,7 +77,7 @@ class DragDropSource {
}
};
auto dragDropSource(auto const&cb, ImGuiDragDropFlags const flags = 0) noexcept {
auto dragDropSource(auto const &cb, ImGuiDragDropFlags const flags = 0) noexcept {
if constexpr(ox::is_same_v<decltype(cb()), ox::Error>) {
if (ig::DragDropSource const tgt{flags}; tgt) [[unlikely]] {
return cb();
@@ -108,7 +108,7 @@ class DragDropTarget {
}
};
auto dragDropTarget(auto const&cb) noexcept {
auto dragDropTarget(auto const &cb) noexcept {
if constexpr(ox::is_same_v<decltype(cb()), ox::Error>) {
if (ig::DragDropTarget const tgt; tgt) [[unlikely]] {
return cb();
@@ -124,7 +124,7 @@ auto dragDropTarget(auto const&cb) noexcept {
class ChildStackItem {
public:
explicit ChildStackItem(ox::CStringViewCR id, ImVec2 const&sz = {}) noexcept;
explicit ChildStackItem(ox::CStringViewCR id, ImVec2 const &sz = {}) noexcept;
~ChildStackItem() noexcept;
};
@@ -146,7 +146,7 @@ class IndentStackItem {
void centerNextWindow(turbine::Context &ctx) noexcept;
bool PushButton(ox::CStringViewCR lbl, ImVec2 const&btnSz = BtnSz) noexcept;
bool PushButton(ox::CStringViewCR lbl, ImVec2 const &btnSz = BtnSz) noexcept;
template<typename Str>
struct TextInput {
@@ -234,7 +234,7 @@ PopupResponse PopupControlsOk(
ox::CStringViewCR ok);
[[nodiscard]]
bool BeginPopup(turbine::Context &ctx, ox::CStringViewCR popupName, bool &show, ImVec2 const&sz = {285, 0});
bool BeginPopup(turbine::Context &ctx, ox::CStringViewCR popupName, bool &show, ImVec2 const &sz = {285, 0});
/**
*
@@ -266,7 +266,7 @@ bool ComboBox(ox::CStringView lbl, ox::Span<const ox::String> list, size_t &sele
*/
bool ComboBox(
ox::CStringViewCR lbl,
std::function<ox::CStringView(size_t)> const&f,
std::function<ox::CStringView(size_t)> const &f,
size_t strCnt,
size_t &selectedIdx) noexcept;
@@ -278,10 +278,10 @@ bool FileComboBox(
bool ListBox(
ox::CStringViewCR name,
std::function<ox::CStringView(size_t)> const&f,
std::function<ox::CStringView(size_t)> const &f,
size_t strCnt,
size_t &selIdx,
ImVec2 const&sz = {0, 0}) noexcept;
ImVec2 const &sz = {0, 0}) noexcept;
/**
*
@@ -290,7 +290,7 @@ bool ListBox(
* @param selIdx
* @return true if new value selected, false otherwise
*/
bool ListBox(ox::CStringViewCR name, ox::SpanView<ox::String> const&list, size_t &selIdx) noexcept;
bool ListBox(ox::CStringViewCR name, ox::SpanView<ox::String> const &list, size_t &selIdx) noexcept;
class FilePicker {
private:
@@ -306,7 +306,7 @@ class FilePicker {
studio::Context &sctx,
ox::StringParam title,
ox::StringParam fileExt,
ImVec2 const&size = {}) noexcept;
ImVec2 const &size = {}) noexcept;
void draw() noexcept;
@@ -348,6 +348,8 @@ class QuestionPopup: public Popup {
QuestionPopup(ox::StringParam title, ox::StringParam question) noexcept;
void setQuestion(ox::StringParam msg) noexcept;
void draw(Context &ctx) noexcept override;
};

View File

@@ -109,7 +109,7 @@ class ItemMaker {
return m_typeDisplayName;
}
bool installTemplate(ox::UPtr<ItemTemplate> &tmpl) {
bool installTemplate(ox::UPtr<ItemTemplate> &&tmpl) {
if (typeName() == tmpl->typeName() &&
typeVersion() <= tmpl->typeVersion()) {
m_templates.emplace_back(std::move(tmpl));
@@ -120,10 +120,6 @@ class ItemMaker {
return false;
}
bool installTemplate(ox::UPtr<ItemTemplate> &&tmpl) {
return installTemplate(tmpl);
}
constexpr ox::Vector<ox::UPtr<ItemTemplate>> const&itemTemplates() const noexcept {
return m_templates;
}

View File

@@ -1,6 +1,7 @@
add_library(
Studio
configio.cpp
context.cpp
editor.cpp
filepickerpopup.cpp
filetreemodel.cpp

View 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;
}
}
}

View File

@@ -72,8 +72,10 @@ bool BaseEditor::exportable() const noexcept {
}
void BaseEditor::setCutEnabled(bool v) {
m_cutEnabled = v;
cutEnabledChanged.emit(v);
if (m_cutEnabled != v) {
m_cutEnabled = v;
cutEnabledChanged.emit(v);
}
}
bool BaseEditor::cutEnabled() const noexcept {
@@ -81,8 +83,10 @@ bool BaseEditor::cutEnabled() const noexcept {
}
void BaseEditor::setCopyEnabled(bool v) {
m_copyEnabled = v;
copyEnabledChanged.emit(v);
if (m_copyEnabled != v) {
m_copyEnabled = v;
copyEnabledChanged.emit(v);
}
}
bool BaseEditor::copyEnabled() const noexcept {
@@ -90,8 +94,10 @@ bool BaseEditor::copyEnabled() const noexcept {
}
void BaseEditor::setPasteEnabled(bool v) {
m_pasteEnabled = v;
pasteEnabledChanged.emit(v);
if (m_pasteEnabled != v) {
m_pasteEnabled = v;
pasteEnabledChanged.emit(v);
}
}
bool BaseEditor::pasteEnabled() const noexcept {

View File

@@ -8,6 +8,15 @@
namespace studio {
FilePickerPopup::Explorer::Explorer(keel::Context &kctx):
FileExplorer{kctx} {
}
void FilePickerPopup::Explorer::fileOpened(ox::StringViewCR) const noexcept {
opened = true;
}
FilePickerPopup::FilePickerPopup(
ox::StringParam name,
keel::Context &kctx,
@@ -41,6 +50,7 @@ void FilePickerPopup::refresh() noexcept {
void FilePickerPopup::open() noexcept {
refresh();
m_open = true;
m_explorer.opened = false;
}
void FilePickerPopup::close() noexcept {
@@ -60,16 +70,22 @@ ox::Optional<ox::String> FilePickerPopup::draw(Context &ctx) noexcept {
if (ig::BeginPopup(ctx.tctx, m_name, m_open, {380, 340})) {
auto const vp = ImGui::GetContentRegionAvail();
m_explorer.draw(ctx, {vp.x, vp.y - 30});
if (ig::PopupControlsOkCancel(m_open) == ig::PopupResponse::OK) {
auto p = m_explorer.selectedPath();
if (p) {
out.emplace(*p);
}
close();
if (ig::PopupControlsOkCancel(m_open) == ig::PopupResponse::OK || m_explorer.opened) {
out = handlePick();
}
ImGui::EndPopup();
}
return out;
}
ox::Optional<ox::String> FilePickerPopup::handlePick() noexcept {
ox::Optional<ox::String> out;
auto p = m_explorer.selectedPath();
if (p) {
out.emplace(*p);
}
close();
return out;
}
}

View File

@@ -10,7 +10,7 @@
namespace studio::ig {
ChildStackItem::ChildStackItem(ox::CStringViewCR id, ImVec2 const&sz) noexcept {
ChildStackItem::ChildStackItem(ox::CStringViewCR id, ImVec2 const &sz) noexcept {
ImGui::BeginChild(id.c_str(), sz);
}
@@ -50,7 +50,7 @@ void centerNextWindow(turbine::Context &ctx) noexcept {
ImGui::SetNextWindowPos(ImVec2(screenW / mod, screenH / mod), ImGuiCond_Always, ImVec2(0.5f, 0.5f));
}
bool PushButton(ox::CStringViewCR lbl, ImVec2 const&btnSz) noexcept {
bool PushButton(ox::CStringViewCR lbl, ImVec2 const &btnSz) noexcept {
return ImGui::Button(lbl.c_str(), btnSz);
}
@@ -101,7 +101,7 @@ PopupResponse PopupControlsOk(
return out;
}
bool BeginPopup(turbine::Context &ctx, ox::CStringViewCR popupName, bool &show, ImVec2 const&sz) {
bool BeginPopup(turbine::Context &ctx, ox::CStringViewCR popupName, bool &show, ImVec2 const &sz) {
constexpr auto modalFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
centerNextWindow(ctx);
ImGui::OpenPopup(popupName.c_str());
@@ -149,7 +149,7 @@ bool ComboBox(
bool ComboBox(
ox::CStringViewCR lbl,
std::function<ox::CStringView(size_t)> const&f,
std::function<ox::CStringView(size_t)> const &f,
size_t strCnt,
size_t &selectedIdx) noexcept {
bool out{};
@@ -172,16 +172,16 @@ bool FileComboBox(
Context &sctx,
ox::StringViewCR fileExt,
size_t &selectedIdx) noexcept {
auto const&list = sctx.project->fileList(fileExt);
auto const &list = sctx.project->fileList(fileExt);
return ComboBox(lbl, list, selectedIdx);
}
bool ListBox(
ox::CStringViewCR name,
std::function<ox::CStringView(size_t)> const&f,
std::function<ox::CStringView(size_t)> const &f,
size_t const strCnt,
size_t &selIdx,
ImVec2 const&sz) noexcept {
ImVec2 const &sz) noexcept {
auto out = false;
if (ImGui::BeginListBox(name.c_str(), sz)) {
for (size_t i = 0; i < strCnt; ++i) {
@@ -199,13 +199,13 @@ bool ListBox(
return out;
}
bool ListBox(ox::CStringViewCR name, ox::SpanView<ox::String> const&list, size_t &selIdx) noexcept {
bool ListBox(ox::CStringViewCR name, ox::SpanView<ox::String> const &list, size_t &selIdx) noexcept {
return ListBox(name, [list](size_t i) -> ox::CStringView {
return list[i];
}, list.size(), selIdx);
}
bool ListBox(ox::CStringViewCR name, ox::SpanView<ox::CStringView> const&list, size_t &selIdx) noexcept {
bool ListBox(ox::CStringViewCR name, ox::SpanView<ox::CStringView> const &list, size_t &selIdx) noexcept {
return ListBox(name, [list](size_t i) -> ox::CStringView {
return list[i];
}, list.size(), selIdx);
@@ -216,7 +216,7 @@ FilePicker::FilePicker(
Context &sctx,
ox::StringParam title,
ox::StringParam fileExt,
ImVec2 const&size) noexcept:
ImVec2 const &size) noexcept:
m_sctx(sctx),
m_title(std::move(title)),
m_fileExt(std::move(fileExt)),
@@ -230,7 +230,7 @@ void FilePicker::draw() noexcept {
auto constexpr popupSz = ImVec2{450.f, 0};
IDStackItem const idStackItem(m_title);
if (BeginPopup(m_sctx.tctx, m_title, m_show, popupSz)) {
auto const&list = m_sctx.project->fileList(m_fileExt);
auto const &list = m_sctx.project->fileList(m_fileExt);
size_t selIdx{};
ComboBox(m_title, list, selIdx);
if (PopupControlsOkCancel(popupSz.x, m_show) == ig::PopupResponse::OK) {
@@ -267,6 +267,10 @@ QuestionPopup::QuestionPopup(ox::StringParam title, ox::StringParam question) no
m_question{std::move(question)} {
}
void QuestionPopup::setQuestion(ox::StringParam msg) noexcept {
m_question = std::move(msg);
}
void QuestionPopup::draw(Context &ctx) noexcept {
switch (m_stage) {
case Stage::Closed:

View File

@@ -101,8 +101,8 @@ ox::Result<ox::FileStat> Project::stat(ox::StringViewCR path) const noexcept {
ox::Error Project::copyItem(ox::StringViewCR src, ox::StringViewCR dest) noexcept {
OX_REQUIRE_M(buff, loadBuff(src));
OX_REQUIRE(id, keel::regenerateUuidHeader(buff));
OX_RETURN_ERROR(writeBuff(dest, buff));
createUuidMapping(m_kctx, dest, id);
OX_RETURN_ERROR(writeBuff(dest, ox::BufferView{buff} + keel::K1HdrSz));
return {};
}