Squashed 'deps/nostalgia/' changes from a3d6a58c..d68e6493
d68e6493 [nostalgia/core/studio/tilesheeteditor] Add support for dragging palette to palette selector 1cbc5762 [studio] Complete drag/drop support for files 500b9356 [studio] Make new dir window OK on Enter key 800ca851 [ox/std] Fix possible error that occurs with appending on boundary of small string size cc466a9f [studio] Add support for adding and deleting directories 9d115584 [nostalgia] Rename player from 'nostalgia' to 'Nostalgia' a2139c09 [studio] Cleanup unused member a3e5f27a [ox/std] Fix Mac build 643f95ec [studio] Add confirmation dialog for file deletion, move deletion to Project 69241476 [studio] Add ability to add file through dir context menu 6e2b4fa7 [nostalgia] Cleanup player run in Makefile 4e5c7499 [studio] Add support for deleting files 66229de7 [ox/fs] FileSystem fixes with removing files 7eb37c53 [nostalgia/core/studio/paletteeditor] Fix adding page if there is no existing page 7a21b207 [nostalgia/core] Replace ContextDeleter with safeDelete(Context*) 894be237 [ox/std] Drop ox:: qualifier from safeDelete function for pointee 92e9d9cb [keel,studio] Add support for New Item templates b29b9a9b [ox/std] Add UAnyPtr 721f8442 [nostalgia/core/studio/tilesheeteditor] Fix subsheet and palette scrolling git-subtree-dir: deps/nostalgia git-subtree-split: d68e64931b37d7d8bbaff7b43bf131c7acf2aa97
This commit is contained in:
parent
50b1ed33df
commit
7b7d59cf63
16
Makefile
16
Makefile
@ -5,23 +5,27 @@ BUILDCORE_PATH=deps/buildcore
|
||||
include ${BUILDCORE_PATH}/base.mk
|
||||
|
||||
ifeq ($(BC_VAR_OS),darwin)
|
||||
NOSTALGIA_STUDIO=./build/${BC_VAR_CURRENT_BUILD}/bin/${BC_VAR_PROJECT_NAME_CAP}Studio.app/Contents/MacOS/${BC_VAR_PROJECT_NAME_CAP}Studio
|
||||
PROJECT_STUDIO=./build/${BC_VAR_CURRENT_BUILD}/bin/${BC_VAR_PROJECT_NAME_CAP}Studio.app/Contents/MacOS/${BC_VAR_PROJECT_NAME_CAP}Studio
|
||||
MGBA=/Applications/mGBA.app/Contents/MacOS/mGBA
|
||||
else
|
||||
NOSTALGIA_STUDIO=./build/${BC_VAR_CURRENT_BUILD}/bin/${BC_VAR_PROJECT_NAME_CAP}Studio
|
||||
PROJECT_STUDIO=./build/${BC_VAR_CURRENT_BUILD}/bin/${BC_VAR_PROJECT_NAME_CAP}Studio
|
||||
MGBA=mgba-qt
|
||||
endif
|
||||
PROJECT_PLAYER=./build/${BC_VAR_CURRENT_BUILD}/bin/${BC_VAR_PROJECT_NAME_CAP}
|
||||
|
||||
.PHONY: pkg-gba
|
||||
pkg-gba: build
|
||||
${BC_CMD_ENVRUN} ${BC_PY3} ./util/scripts/pkg-gba.py sample_project ${BC_VAR_PROJECT_NAME}
|
||||
|
||||
.PHONY: build-player
|
||||
build-player:
|
||||
${BC_CMD_CMAKE_BUILD} ${BC_VAR_BUILD_PATH} ${BC_VAR_PROJECT_NAME_CAP}
|
||||
.PHONY: run
|
||||
run: build
|
||||
./build/${BC_VAR_CURRENT_BUILD}/bin/${BC_VAR_PROJECT_NAME} sample_project
|
||||
run: build-player
|
||||
${PROJECT_PLAYER} sample_project
|
||||
.PHONY: run-studio
|
||||
run-studio: build
|
||||
${NOSTALGIA_STUDIO}
|
||||
${PROJECT_STUDIO}
|
||||
.PHONY: gba-run
|
||||
gba-run: pkg-gba
|
||||
${MGBA} ${BC_VAR_PROJECT_NAME}.gba
|
||||
@ -30,7 +34,7 @@ debug: build
|
||||
${BC_CMD_HOST_DEBUGGER} ./build/${BC_VAR_CURRENT_BUILD}/bin/${BC_VAR_PROJECT_NAME} sample_project
|
||||
.PHONY: debug-studio
|
||||
debug-studio: build
|
||||
${BC_CMD_HOST_DEBUGGER} ${NOSTALGIA_STUDIO}
|
||||
${BC_CMD_HOST_DEBUGGER} ${PROJECT_STUDIO}
|
||||
|
||||
.PHONY: configure-gba
|
||||
configure-gba:
|
||||
|
12
deps/ox/src/ox/fs/filesystem/filesystem.cpp
vendored
12
deps/ox/src/ox/fs/filesystem/filesystem.cpp
vendored
@ -63,18 +63,6 @@ Error FileSystem::read(const FileAddress &addr, std::size_t readStart, std::size
|
||||
}
|
||||
}
|
||||
|
||||
Error FileSystem::remove(const FileAddress &addr, bool recursive) noexcept {
|
||||
switch (addr.type()) {
|
||||
case FileAddressType::Inode:
|
||||
return remove(addr.getInode().value, recursive);
|
||||
case FileAddressType::ConstPath:
|
||||
case FileAddressType::Path:
|
||||
return remove(StringView(addr.getPath().value), recursive);
|
||||
default:
|
||||
return ox::Error(1);
|
||||
}
|
||||
}
|
||||
|
||||
Error FileSystem::write(const FileAddress &addr, const void *buffer, uint64_t size, FileType fileType) noexcept {
|
||||
switch (addr.type()) {
|
||||
case FileAddressType::Inode:
|
||||
|
50
deps/ox/src/ox/fs/filesystem/filesystem.hpp
vendored
50
deps/ox/src/ox/fs/filesystem/filesystem.hpp
vendored
@ -57,9 +57,9 @@ class FileSystem {
|
||||
|
||||
virtual Result<Vector<String>> ls(StringViewCR dir) const noexcept = 0;
|
||||
|
||||
virtual Error remove(StringViewCR path, bool recursive) noexcept = 0;
|
||||
|
||||
Error remove(const FileAddress &addr, bool recursive = false) noexcept;
|
||||
Error remove(StringViewCR path, bool recursive = false) noexcept {
|
||||
return removePath(path, recursive);
|
||||
}
|
||||
|
||||
virtual Error resize(uint64_t size, void *buffer) noexcept = 0;
|
||||
|
||||
@ -142,6 +142,8 @@ class FileSystem {
|
||||
|
||||
virtual Error readFileInodeRange(uint64_t inode, std::size_t readStart, std::size_t readSize, void *buffer, std::size_t *size) noexcept = 0;
|
||||
|
||||
virtual Error removePath(StringViewCR path, bool recursive) noexcept = 0;
|
||||
|
||||
virtual Error writeFilePath(StringViewCR path, const void *buffer, uint64_t size, FileType fileType) noexcept = 0;
|
||||
|
||||
virtual Error writeFileInode(uint64_t inode, const void *buffer, uint64_t size, FileType fileType) noexcept = 0;
|
||||
@ -209,6 +211,8 @@ class FileSystemTemplate: public MemFS {
|
||||
|
||||
Error readFileInodeRange(uint64_t inode, std::size_t readStart, std::size_t readSize, void *buffer, std::size_t *size) noexcept override;
|
||||
|
||||
Error removePath(StringViewCR path, bool recursive) noexcept override;
|
||||
|
||||
Result<const char*> directAccessInode(uint64_t) const noexcept override;
|
||||
|
||||
Result<Vector<String>> ls(StringViewCR dir) const noexcept override;
|
||||
@ -216,8 +220,6 @@ class FileSystemTemplate: public MemFS {
|
||||
template<typename F>
|
||||
Error ls(StringViewCR path, F cb) const;
|
||||
|
||||
Error remove(StringViewCR path, bool recursive) noexcept override;
|
||||
|
||||
/**
|
||||
* Resizes FileSystem to minimum possible size.
|
||||
*/
|
||||
@ -356,6 +358,25 @@ Error FileSystemTemplate<FileStore, Directory>::readFileInodeRange(uint64_t inod
|
||||
return m_fs.read(inode, readStart, readSize, reinterpret_cast<uint8_t*>(buffer), size);
|
||||
}
|
||||
|
||||
template<typename FileStore, typename Directory>
|
||||
Error FileSystemTemplate<FileStore, Directory>::removePath(StringViewCR path, bool recursive) noexcept {
|
||||
OX_REQUIRE(fd, fileSystemData());
|
||||
Directory rootDir(m_fs, fd.rootDirInode);
|
||||
OX_REQUIRE(inode, rootDir.find(path));
|
||||
OX_REQUIRE(st, statInode(inode));
|
||||
if (st.fileType == FileType::NormalFile || recursive) {
|
||||
if (auto err = rootDir.remove(path)) {
|
||||
// removal failed, try putting the index back
|
||||
oxLogError(rootDir.write(path, inode));
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
oxTrace("FileSystemTemplate.remove.fail", "Tried to remove directory without recursive setting.");
|
||||
return ox::Error(1);
|
||||
}
|
||||
return ox::Error(0);
|
||||
}
|
||||
|
||||
template<typename FileStore, typename Directory>
|
||||
Result<const char*> FileSystemTemplate<FileStore, Directory>::directAccessInode(uint64_t inode) const noexcept {
|
||||
auto data = m_fs.read(inode);
|
||||
@ -384,25 +405,6 @@ Error FileSystemTemplate<FileStore, Directory>::ls(StringViewCR path, F cb) cons
|
||||
return dir.ls(cb);
|
||||
}
|
||||
|
||||
template<typename FileStore, typename Directory>
|
||||
Error FileSystemTemplate<FileStore, Directory>::remove(StringViewCR path, bool recursive) noexcept {
|
||||
OX_REQUIRE(fd, fileSystemData());
|
||||
Directory rootDir(m_fs, fd.rootDirInode);
|
||||
OX_REQUIRE(inode, rootDir.find(path));
|
||||
OX_REQUIRE(st, statInode(inode));
|
||||
if (st.fileType == FileType::NormalFile || recursive) {
|
||||
if (auto err = rootDir.remove(path)) {
|
||||
// removal failed, try putting the index back
|
||||
oxLogError(rootDir.write(path, inode));
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
oxTrace("FileSystemTemplate.remove.fail", "Tried to remove directory without recursive setting.");
|
||||
return ox::Error(1);
|
||||
}
|
||||
return ox::Error(0);
|
||||
}
|
||||
|
||||
template<typename FileStore, typename Directory>
|
||||
Error FileSystemTemplate<FileStore, Directory>::resize() noexcept {
|
||||
return m_fs.resize();
|
||||
|
16
deps/ox/src/ox/fs/filesystem/passthroughfs.cpp
vendored
16
deps/ox/src/ox/fs/filesystem/passthroughfs.cpp
vendored
@ -75,14 +75,6 @@ Result<Vector<String>> PassThroughFS::ls(StringViewCR dir) const noexcept {
|
||||
return out;
|
||||
}
|
||||
|
||||
Error PassThroughFS::remove(StringViewCR path, bool recursive) noexcept {
|
||||
if (recursive) {
|
||||
return ox::Error(std::filesystem::remove_all(m_path / stripSlash(path)) != 0);
|
||||
} else {
|
||||
return ox::Error(std::filesystem::remove(m_path / stripSlash(path)) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
Error PassThroughFS::resize(uint64_t, void*) noexcept {
|
||||
// unsupported
|
||||
return ox::Error(1, "resize is not supported by PassThroughFS");
|
||||
@ -167,6 +159,14 @@ Error PassThroughFS::readFileInodeRange(uint64_t, std::size_t, std::size_t, void
|
||||
return ox::Error(1, "read(uint64_t, std::size_t, std::size_t, void*, std::size_t*) is not supported by PassThroughFS");
|
||||
}
|
||||
|
||||
Error PassThroughFS::removePath(StringViewCR path, bool const recursive) noexcept {
|
||||
if (recursive) {
|
||||
return ox::Error{std::filesystem::remove_all(m_path / stripSlash(path)) == 0};
|
||||
} else {
|
||||
return ox::Error{!std::filesystem::remove(m_path / stripSlash(path))};
|
||||
}
|
||||
}
|
||||
|
||||
Error PassThroughFS::writeFilePath(StringViewCR path, const void *buffer, uint64_t size, FileType) noexcept {
|
||||
const auto p = (m_path / stripSlash(path));
|
||||
try {
|
||||
|
@ -45,8 +45,6 @@ class PassThroughFS: public FileSystem {
|
||||
template<typename F>
|
||||
Error ls(StringViewCR dir, F cb) const noexcept;
|
||||
|
||||
Error remove(StringViewCR path, bool recursive) noexcept override;
|
||||
|
||||
Error resize(uint64_t size, void *buffer) noexcept override;
|
||||
|
||||
Result<FileStat> statInode(uint64_t inode) const noexcept override;
|
||||
@ -75,6 +73,8 @@ class PassThroughFS: public FileSystem {
|
||||
|
||||
Error readFileInodeRange(uint64_t inode, std::size_t readStart, std::size_t readSize, void *buffer, std::size_t *size) noexcept override;
|
||||
|
||||
Error removePath(StringViewCR path, bool recursive) noexcept override;
|
||||
|
||||
Error writeFilePath(StringViewCR path, const void *buffer, uint64_t size, FileType fileType) noexcept override;
|
||||
|
||||
Error writeFileInode(uint64_t inode, const void *buffer, uint64_t size, FileType fileType) noexcept override;
|
||||
|
91
deps/ox/src/ox/std/anyptr.hpp
vendored
91
deps/ox/src/ox/std/anyptr.hpp
vendored
@ -15,18 +15,22 @@
|
||||
|
||||
namespace ox {
|
||||
|
||||
class AnyPtr {
|
||||
namespace detail {
|
||||
|
||||
template<bool unique>
|
||||
class AnyPtrT {
|
||||
private:
|
||||
struct WrapBase {
|
||||
virtual constexpr ~WrapBase() = default;
|
||||
virtual constexpr WrapBase *copyTo(ox::Span<char> s) noexcept = 0;
|
||||
virtual constexpr operator bool() const noexcept = 0;
|
||||
virtual void free() noexcept = 0;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct Wrap: public WrapBase {
|
||||
struct Wrap final: WrapBase {
|
||||
T *data{};
|
||||
constexpr Wrap(T *pData) noexcept: data(pData) {
|
||||
explicit constexpr Wrap(T *pData) noexcept: data(pData) {
|
||||
}
|
||||
constexpr WrapBase *copyTo(ox::Span<char> s) noexcept override {
|
||||
oxAssert(s.size() >= sizeof(Wrap), "too small buffer");
|
||||
@ -39,39 +43,60 @@ class AnyPtr {
|
||||
constexpr operator bool() const noexcept override {
|
||||
return data != nullptr;
|
||||
}
|
||||
constexpr void free() noexcept override {
|
||||
safeDelete(data);
|
||||
data = {};
|
||||
}
|
||||
};
|
||||
|
||||
WrapBase *m_wrapPtr{};
|
||||
ox::Array<char, sizeof(Wrap<void*>)> m_wrapData;
|
||||
|
||||
public:
|
||||
constexpr AnyPtr() noexcept = default;
|
||||
constexpr AnyPtrT() noexcept = default;
|
||||
|
||||
template<typename T>
|
||||
constexpr AnyPtr(T *ptr) noexcept {
|
||||
constexpr AnyPtrT(T *ptr) noexcept {
|
||||
if (std::is_constant_evaluated()) {
|
||||
m_wrapPtr = new Wrap(ptr);
|
||||
m_wrapPtr = new Wrap<T>(ptr);
|
||||
} else {
|
||||
m_wrapPtr = new(m_wrapData.data()) Wrap(ptr);
|
||||
m_wrapPtr = new(m_wrapData.data()) Wrap<T>(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr AnyPtr(AnyPtr const&other) noexcept {
|
||||
constexpr AnyPtrT(AnyPtrT const&other) noexcept {
|
||||
if (other) {
|
||||
m_wrapPtr = other.m_wrapPtr->copyTo(m_wrapData);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr ~AnyPtr() noexcept {
|
||||
constexpr AnyPtrT(AnyPtrT &&other) noexcept {
|
||||
if (other) {
|
||||
m_wrapPtr = other.m_wrapPtr->copyTo(m_wrapData);
|
||||
if (std::is_constant_evaluated()) {
|
||||
ox::safeDelete(m_wrapPtr);
|
||||
}
|
||||
other.m_wrapPtr = {};
|
||||
}
|
||||
}
|
||||
|
||||
constexpr ~AnyPtrT() noexcept {
|
||||
if constexpr(unique) {
|
||||
free();
|
||||
}
|
||||
if (std::is_constant_evaluated()) {
|
||||
ox::safeDelete(m_wrapPtr);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr AnyPtr &operator=(T *ptr) noexcept {
|
||||
if (std::is_constant_evaluated()) {
|
||||
constexpr AnyPtrT &operator=(T *ptr) noexcept {
|
||||
if constexpr(unique) {
|
||||
free();
|
||||
} else if (std::is_constant_evaluated()) {
|
||||
ox::safeDelete(m_wrapPtr);
|
||||
}
|
||||
if (std::is_constant_evaluated()) {
|
||||
m_wrapPtr = new Wrap(ptr);
|
||||
} else {
|
||||
m_wrapPtr = new(m_wrapData.data()) Wrap(ptr);
|
||||
@ -79,10 +104,14 @@ class AnyPtr {
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr AnyPtr &operator=(AnyPtr const&ptr) noexcept {
|
||||
constexpr AnyPtrT &operator=(AnyPtrT const&ptr) noexcept {
|
||||
if (this != &ptr) {
|
||||
if (ptr) {
|
||||
if constexpr(unique) {
|
||||
free();
|
||||
} else if (std::is_constant_evaluated()) {
|
||||
ox::safeDelete(m_wrapPtr);
|
||||
}
|
||||
if (ptr) {
|
||||
m_wrapPtr = ptr.m_wrapPtr->copyTo(m_wrapData);
|
||||
} else {
|
||||
m_wrapPtr = nullptr;
|
||||
@ -91,10 +120,40 @@ class AnyPtr {
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr AnyPtrT &operator=(AnyPtrT &&ptr) noexcept {
|
||||
if (this != &ptr) {
|
||||
if constexpr(unique) {
|
||||
free();
|
||||
} else if (std::is_constant_evaluated()) {
|
||||
ox::safeDelete(m_wrapPtr);
|
||||
}
|
||||
if (ptr) {
|
||||
m_wrapPtr = ptr.m_wrapPtr->copyTo(m_wrapData);
|
||||
if (std::is_constant_evaluated()) {
|
||||
ox::safeDelete(ptr.m_wrapPtr);
|
||||
ptr.m_wrapPtr = nullptr;
|
||||
}
|
||||
} else {
|
||||
m_wrapPtr = nullptr;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr operator bool() const noexcept {
|
||||
return m_wrapPtr && *m_wrapPtr;
|
||||
}
|
||||
|
||||
constexpr void free() noexcept {
|
||||
if (m_wrapPtr) {
|
||||
m_wrapPtr->free();
|
||||
}
|
||||
if (std::is_constant_evaluated()) {
|
||||
ox::safeDelete(m_wrapPtr);
|
||||
}
|
||||
m_wrapPtr = nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr T *get() const noexcept {
|
||||
@ -104,6 +163,12 @@ class AnyPtr {
|
||||
return dynamic_cast<Wrap<T>*>(m_wrapPtr)->data;
|
||||
#endif
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using AnyPtr = detail::AnyPtrT<false>;
|
||||
using UAnyPtr = detail::AnyPtrT<true>;
|
||||
|
||||
}
|
8
deps/ox/src/ox/std/string.hpp
vendored
8
deps/ox/src/ox/std/string.hpp
vendored
@ -423,9 +423,10 @@ constexpr BasicString<SmallStringSize_v> BasicString<SmallStringSize_v>::operato
|
||||
const std::size_t strLen = src.len();
|
||||
const auto currentLen = len();
|
||||
BasicString<SmallStringSize_v> cpy(currentLen + strLen);
|
||||
cpy.m_buff.resize(m_buff.size() + strLen);
|
||||
cpy.m_buff.resize(m_buff.size() + strLen + 1);
|
||||
ox::listcpy(&cpy.m_buff[0], m_buff.data(), currentLen);
|
||||
ox::listcpy(&cpy.m_buff[currentLen], src.data(), strLen + 1);
|
||||
ox::listcpy(&cpy.m_buff[currentLen], src.data(), strLen);
|
||||
cpy.m_buff[cpy.m_buff.size() - 1] = 0;
|
||||
return cpy;
|
||||
}
|
||||
|
||||
@ -436,7 +437,8 @@ constexpr BasicString<SmallStringSize_v> BasicString<SmallStringSize_v>::operato
|
||||
BasicString<SmallStringSize_v> cpy(currentLen + strLen);
|
||||
cpy.m_buff.resize(m_buff.size() + strLen);
|
||||
ox::listcpy(&cpy.m_buff[0], m_buff.data(), currentLen);
|
||||
ox::listcpy(&cpy.m_buff[currentLen], src.data(), strLen + 1);
|
||||
ox::listcpy(&cpy.m_buff[currentLen], src.data(), strLen);
|
||||
cpy.m_buff[cpy.m_buff.size() - 1] = 0;
|
||||
return cpy;
|
||||
}
|
||||
|
||||
|
@ -17,11 +17,9 @@ namespace nostalgia::core {
|
||||
|
||||
class Context;
|
||||
|
||||
struct ContextDeleter {
|
||||
void operator()(Context *p) noexcept;
|
||||
};
|
||||
void safeDelete(Context *ctx) noexcept;
|
||||
|
||||
using ContextUPtr = ox::UPtr<Context, ContextDeleter>;
|
||||
using ContextUPtr = ox::UPtr<Context>;
|
||||
|
||||
ox::Result<ContextUPtr> init(turbine::Context &tctx, InitParams const¶ms = {}) noexcept;
|
||||
|
||||
|
@ -10,8 +10,8 @@
|
||||
|
||||
namespace nostalgia::core {
|
||||
|
||||
void ContextDeleter::operator()(Context *p) noexcept {
|
||||
ox::safeDelete(p);
|
||||
void safeDelete(Context *ctx) noexcept {
|
||||
delete ctx;
|
||||
}
|
||||
|
||||
Context::Context(turbine::Context &tctx) noexcept: turbineCtx(tctx) {
|
||||
|
@ -7,8 +7,8 @@
|
||||
|
||||
namespace nostalgia::core {
|
||||
|
||||
void ContextDeleter::operator()(Context *p) noexcept {
|
||||
ox::safeDelete(p);
|
||||
void safeDelete(Context *ctx) noexcept {
|
||||
delete ctx;
|
||||
}
|
||||
|
||||
Context::Context(turbine::Context &tctx, InitParams const¶ms) noexcept:
|
||||
|
@ -1,6 +1,7 @@
|
||||
target_sources(
|
||||
NostalgiaCore-Studio PRIVATE
|
||||
commands/addcolorcommand.cpp
|
||||
commands/addpagecommand.cpp
|
||||
commands/applycolorallpagescommand.cpp
|
||||
commands/duplicatepagecommand.cpp
|
||||
commands/movecolorcommand.cpp
|
||||
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include "commands.hpp"
|
||||
|
||||
#include "addpagecommand.hpp"
|
||||
|
||||
namespace nostalgia::core {
|
||||
|
||||
AddPageCommand::AddPageCommand(Palette &pal) noexcept:
|
||||
m_pal(pal) {}
|
||||
|
||||
int AddPageCommand::commandId() const noexcept {
|
||||
return static_cast<int>(PaletteEditorCommandId::AddPage);
|
||||
}
|
||||
|
||||
ox::Error AddPageCommand::redo() noexcept {
|
||||
m_pal.pages.emplace_back(ox::sfmt("Page {}", m_pal.pages.size() + 1), ox::Vector<PaletteColor>{});
|
||||
return {};
|
||||
}
|
||||
|
||||
ox::Error AddPageCommand::undo() noexcept {
|
||||
m_pal.pages.pop_back();
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <studio/studio.hpp>
|
||||
|
||||
#include <nostalgia/core/palette.hpp>
|
||||
|
||||
namespace nostalgia::core {
|
||||
|
||||
class AddPageCommand: public studio::UndoCommand {
|
||||
private:
|
||||
Palette &m_pal;
|
||||
|
||||
public:
|
||||
explicit AddPageCommand(Palette &pal) noexcept;
|
||||
|
||||
~AddPageCommand() noexcept override = default;
|
||||
|
||||
[[nodiscard]]
|
||||
int commandId() const noexcept final;
|
||||
|
||||
ox::Error redo() noexcept final;
|
||||
|
||||
ox::Error undo() noexcept final;
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -9,6 +9,7 @@ namespace nostalgia::core {
|
||||
enum class PaletteEditorCommandId {
|
||||
ApplyColorAllPages,
|
||||
RenamePage,
|
||||
AddPage,
|
||||
DuplicatePage,
|
||||
RemovePage,
|
||||
AddColor,
|
||||
|
@ -23,7 +23,7 @@ int DuplicatePageCommand::commandId() const noexcept {
|
||||
}
|
||||
|
||||
ox::Error DuplicatePageCommand::redo() noexcept {
|
||||
m_pal.pages.emplace(m_dstIdx, "", std::move(m_page));
|
||||
m_pal.pages.emplace(m_dstIdx, ox::sfmt("Page {}", m_pal.pages.size() + 1), std::move(m_page));
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <keel/media.hpp>
|
||||
|
||||
#include "commands/addcolorcommand.hpp"
|
||||
#include "commands/addpagecommand.hpp"
|
||||
#include "commands/applycolorallpagescommand.hpp"
|
||||
#include "commands/duplicatepagecommand.hpp"
|
||||
#include "commands/movecolorcommand.hpp"
|
||||
@ -196,7 +197,11 @@ void PaletteEditorImGui::drawPagesEditor() noexcept {
|
||||
constexpr auto toolbarHeight = 40;
|
||||
auto const btnSz = ImVec2{paneSz.x / 4 - 5.5f, 24};
|
||||
if (ImGui::Button("Add", btnSz)) {
|
||||
std::ignore = pushCommand<DuplicatePageCommand>(m_pal, 0u, m_pal.pages.size());
|
||||
if (m_pal.pages.empty()) {
|
||||
std::ignore = pushCommand<AddPageCommand>(m_pal);
|
||||
} else {
|
||||
std::ignore = pushCommand<DuplicatePageCommand>(m_pal, 0u, m_pal.pages.size());
|
||||
}
|
||||
m_page = m_pal.pages.size() - 1;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
@ -234,7 +234,7 @@ void TileSheetEditorImGui::draw(studio::StudioContext&) noexcept {
|
||||
ImGui::BeginChild("SubSheets", {s_palViewWidth - 24, ySize / 2.f}, true);
|
||||
{
|
||||
static constexpr auto btnHeight = ig::BtnSz.y;
|
||||
auto const btnSize = ImVec2{btnHeight, btnHeight};
|
||||
auto constexpr btnSize = ImVec2{btnHeight, btnHeight};
|
||||
if (ig::PushButton("+", btnSize)) {
|
||||
auto insertOnIdx = m_model.activeSubSheetIdx();
|
||||
auto const&parent = m_model.activeSubSheet();
|
||||
@ -258,16 +258,19 @@ void TileSheetEditorImGui::draw(studio::StudioContext&) noexcept {
|
||||
m_exportMenu.show();
|
||||
}
|
||||
TileSheet::SubSheetIdx path;
|
||||
static constexpr auto flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody;
|
||||
if (ImGui::BeginTable("Subsheets", 4, flags)) {
|
||||
ImGui::TableSetupColumn("Subsheet", ImGuiTableColumnFlags_NoHide);
|
||||
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 25);
|
||||
ImGui::TableSetupColumn("Columns", ImGuiTableColumnFlags_WidthFixed, 50);
|
||||
ImGui::TableSetupColumn("Rows", ImGuiTableColumnFlags_WidthFixed, 50);
|
||||
ImGui::TableHeadersRow();
|
||||
drawSubsheetSelector(m_view.img().subsheet, path);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
static constexpr auto flags =
|
||||
ImGuiTableFlags_RowBg |
|
||||
ImGuiTableFlags_NoBordersInBody |
|
||||
ImGuiTableFlags_ScrollY;
|
||||
if (ImGui::BeginTable("Subsheets", 4, flags)) {
|
||||
ImGui::TableSetupColumn("Subsheet", ImGuiTableColumnFlags_NoHide);
|
||||
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 25);
|
||||
ImGui::TableSetupColumn("Columns", ImGuiTableColumnFlags_WidthFixed, 50);
|
||||
ImGui::TableSetupColumn("Rows", ImGuiTableColumnFlags_WidthFixed, 50);
|
||||
ImGui::TableHeadersRow();
|
||||
drawSubsheetSelector(m_view.img().subsheet, path);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
@ -440,6 +443,16 @@ void TileSheetEditorImGui::drawPaletteMenu() noexcept {
|
||||
if (ig::ComboBox("Palette", files, m_selectedPaletteIdx)) {
|
||||
oxLogError(m_model.setPalette(files[m_selectedPaletteIdx]));
|
||||
}
|
||||
if (ig::DragDropTarget const dragDropTarget; dragDropTarget) {
|
||||
auto const [ref, err] = ig::getDragDropPayload<studio::FileRef>("FileRef");
|
||||
if (!err) {
|
||||
auto const oldVal = m_selectedPaletteIdx;
|
||||
std::ignore = ox::findIdx(files.begin(), files.end(), ref.path).moveTo(m_selectedPaletteIdx);
|
||||
if (oldVal != m_selectedPaletteIdx) {
|
||||
oxLogError(m_model.setPalette(files[m_selectedPaletteIdx]));
|
||||
}
|
||||
}
|
||||
}
|
||||
auto const pages = m_model.pal().pages.size();
|
||||
if (pages > 1) {
|
||||
ig::IndentStackItem const indentStackItem{20};
|
||||
@ -462,8 +475,12 @@ void TileSheetEditorImGui::drawPaletteMenu() noexcept {
|
||||
}
|
||||
}
|
||||
// header
|
||||
auto constexpr palTblFlags =
|
||||
ImGuiTableFlags_RowBg |
|
||||
ImGuiTableFlags_SizingStretchProp |
|
||||
ImGuiTableFlags_ScrollY;
|
||||
if (ImGui::BeginTable(
|
||||
"PaletteTable", 4, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingStretchProp)) {
|
||||
"PaletteTable", 4, palTblFlags)) {
|
||||
ImGui::TableSetupColumn("Idx", 0, 0.6f);
|
||||
ImGui::TableSetupColumn("", 0, 0.22f);
|
||||
ImGui::TableSetupColumn("Name", 0, 3);
|
||||
|
@ -1,26 +1,26 @@
|
||||
add_executable(
|
||||
nostalgia WIN32
|
||||
Nostalgia WIN32
|
||||
app.cpp
|
||||
)
|
||||
|
||||
# enable LTO
|
||||
if(NOT WIN32)
|
||||
set_property(TARGET nostalgia PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||
set_property(TARGET Nostalgia PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||
endif()
|
||||
|
||||
if(COMMAND OBJCOPY_FILE)
|
||||
set_target_properties(nostalgia
|
||||
set_target_properties(Nostalgia
|
||||
PROPERTIES
|
||||
LINK_FLAGS ${LINKER_FLAGS}
|
||||
COMPILER_FLAGS "-mthumb -mthumb-interwork"
|
||||
)
|
||||
|
||||
OBJCOPY_FILE(nostalgia)
|
||||
#PADBIN_FILE(nostalgia)
|
||||
OBJCOPY_FILE(Nostalgia)
|
||||
#PADBIN_FILE(Nostalgia)
|
||||
endif()
|
||||
|
||||
target_link_libraries(
|
||||
nostalgia
|
||||
Nostalgia
|
||||
NostalgiaKeelModules
|
||||
NostalgiaProfile
|
||||
OlympicApplib
|
||||
@ -29,7 +29,7 @@ target_link_libraries(
|
||||
|
||||
install(
|
||||
TARGETS
|
||||
nostalgia
|
||||
Nostalgia
|
||||
DESTINATION
|
||||
bin
|
||||
)
|
||||
|
@ -1,8 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <ox/std/memory.hpp>
|
||||
#include <ox/fs/fs.hpp>
|
||||
|
||||
typename ox::Error run(ox::UniquePtr<ox::FileSystem> &&fs) noexcept;
|
@ -20,6 +20,8 @@ class Wrap {
|
||||
virtual ox::CStringView typeName() const noexcept = 0;
|
||||
[[nodiscard]]
|
||||
virtual int typeVersion() const noexcept = 0;
|
||||
[[nodiscard]]
|
||||
virtual ox::UAnyPtr moveToCopy() noexcept = 0;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
@ -27,6 +29,11 @@ class WrapT: public Wrap {
|
||||
public:
|
||||
[[nodiscard]]
|
||||
virtual constexpr T &obj() noexcept = 0;
|
||||
|
||||
ox::UAnyPtr moveToCopy() noexcept final {
|
||||
return new T{std::move(obj())};
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
@ -184,7 +191,18 @@ ox::Result<ox::UPtr<Wrap>> convert(
|
||||
auto &src,
|
||||
ox::StringViewCR dstTypeName,
|
||||
int const dstTypeVersion) noexcept {
|
||||
return convert(ctx, WrapRef{src}, dstTypeName, dstTypeVersion);
|
||||
WrapRef ref{src};
|
||||
return convert(ctx, static_cast<Wrap&>(ref), dstTypeName, dstTypeVersion);
|
||||
}
|
||||
|
||||
ox::Result<ox::UPtr<Wrap>> convert(
|
||||
keel::Context &ctx,
|
||||
auto const&src,
|
||||
ox::StringViewCR dstTypeName,
|
||||
int const dstTypeVersion) noexcept {
|
||||
auto srcCpy = src;
|
||||
WrapRef ref{srcCpy};
|
||||
return convert(ctx, ref, dstTypeName, dstTypeVersion);
|
||||
}
|
||||
|
||||
template<typename DstType>
|
||||
|
@ -2,8 +2,10 @@ add_library(
|
||||
StudioAppLib
|
||||
aboutpopup.cpp
|
||||
clawviewer.cpp
|
||||
deleteconfirmation.cpp
|
||||
filedialogmanager.cpp
|
||||
main.cpp
|
||||
newdir.cpp
|
||||
newmenu.cpp
|
||||
newproject.cpp
|
||||
projectexplorer.cpp
|
||||
|
55
src/olympic/studio/applib/src/deleteconfirmation.cpp
Normal file
55
src/olympic/studio/applib/src/deleteconfirmation.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <studio/imguiutil.hpp>
|
||||
|
||||
#include "deleteconfirmation.hpp"
|
||||
|
||||
namespace studio {
|
||||
|
||||
DeleteConfirmation::DeleteConfirmation() noexcept {
|
||||
setTitle("Delete Item");
|
||||
}
|
||||
|
||||
void DeleteConfirmation::openPath(ox::StringViewCR path) noexcept {
|
||||
open();
|
||||
m_path = path;
|
||||
}
|
||||
|
||||
void DeleteConfirmation::open() noexcept {
|
||||
m_path = "";
|
||||
m_stage = Stage::Opening;
|
||||
}
|
||||
|
||||
void DeleteConfirmation::close() noexcept {
|
||||
m_stage = Stage::Closed;
|
||||
m_open = false;
|
||||
}
|
||||
|
||||
bool DeleteConfirmation::isOpen() const noexcept {
|
||||
return m_open;
|
||||
}
|
||||
|
||||
void DeleteConfirmation::draw(StudioContext &ctx) noexcept {
|
||||
switch (m_stage) {
|
||||
case Stage::Closed:
|
||||
break;
|
||||
case Stage::Opening:
|
||||
ImGui::OpenPopup(title().c_str());
|
||||
m_stage = Stage::Open;
|
||||
m_open = true;
|
||||
[[fallthrough]];
|
||||
case Stage::Open:
|
||||
drawWindow(ctx.tctx, m_open, [this] {
|
||||
ImGui::Text("Are you sure you want to delete %s?", m_path.c_str());
|
||||
if (ig::PopupControlsOkCancel(m_open, "Yes", "No") != ig::PopupResponse::None) {
|
||||
deleteFile.emit(m_path);
|
||||
close();
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
43
src/olympic/studio/applib/src/deleteconfirmation.hpp
Normal file
43
src/olympic/studio/applib/src/deleteconfirmation.hpp
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/string.hpp>
|
||||
|
||||
#include <studio/context.hpp>
|
||||
#include <studio/popup.hpp>
|
||||
|
||||
namespace studio {
|
||||
|
||||
class DeleteConfirmation final: public Popup {
|
||||
private:
|
||||
enum class Stage {
|
||||
Closed,
|
||||
Opening,
|
||||
Open,
|
||||
};
|
||||
Stage m_stage = Stage::Closed;
|
||||
bool m_open{};
|
||||
ox::String m_path;
|
||||
|
||||
public:
|
||||
ox::Signal<ox::Error(ox::StringViewCR path)> deleteFile;
|
||||
|
||||
DeleteConfirmation() noexcept;
|
||||
|
||||
void openPath(ox::StringViewCR path) noexcept;
|
||||
|
||||
void open() noexcept override;
|
||||
|
||||
void close() noexcept override;
|
||||
|
||||
[[nodiscard]]
|
||||
bool isOpen() const noexcept override;
|
||||
|
||||
void draw(StudioContext &ctx) noexcept override;
|
||||
|
||||
};
|
||||
|
||||
}
|
63
src/olympic/studio/applib/src/newdir.cpp
Normal file
63
src/olympic/studio/applib/src/newdir.cpp
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <studio/imguiutil.hpp>
|
||||
|
||||
#include "newdir.hpp"
|
||||
|
||||
namespace studio {
|
||||
|
||||
NewDir::NewDir() noexcept {
|
||||
setTitle("New Directory");
|
||||
setSize({280, 0});
|
||||
}
|
||||
|
||||
void NewDir::openPath(ox::StringViewCR path) noexcept {
|
||||
open();
|
||||
m_path = path;
|
||||
}
|
||||
|
||||
void NewDir::open() noexcept {
|
||||
m_path = "";
|
||||
m_stage = Stage::Opening;
|
||||
}
|
||||
|
||||
void NewDir::close() noexcept {
|
||||
m_stage = Stage::Closed;
|
||||
m_open = false;
|
||||
}
|
||||
|
||||
bool NewDir::isOpen() const noexcept {
|
||||
return m_open;
|
||||
}
|
||||
|
||||
void NewDir::draw(StudioContext &ctx) noexcept {
|
||||
switch (m_stage) {
|
||||
case Stage::Closed:
|
||||
break;
|
||||
case Stage::Opening:
|
||||
ImGui::OpenPopup(title().c_str());
|
||||
m_open = true;
|
||||
[[fallthrough]];
|
||||
case Stage::Open:
|
||||
drawWindow(ctx.tctx, m_open, [this] {
|
||||
if (m_stage == Stage::Opening) {
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
}
|
||||
ig::InputText("Name", m_str);
|
||||
if (ImGui::IsItemFocused() && ImGui::IsKeyPressed(ImGuiKey_Enter)) {
|
||||
newDir.emit(m_path + "/" + m_str);
|
||||
close();
|
||||
}
|
||||
if (ig::PopupControlsOkCancel(m_open) == ig::PopupResponse::OK) {
|
||||
newDir.emit(m_path + "/" + m_str);
|
||||
close();
|
||||
}
|
||||
});
|
||||
m_stage = Stage::Open;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
49
src/olympic/studio/applib/src/newdir.hpp
Normal file
49
src/olympic/studio/applib/src/newdir.hpp
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/string.hpp>
|
||||
|
||||
#include <studio/context.hpp>
|
||||
#include <studio/popup.hpp>
|
||||
|
||||
namespace studio {
|
||||
|
||||
class NewDir final: public Popup {
|
||||
private:
|
||||
enum class Stage {
|
||||
Closed,
|
||||
Opening,
|
||||
Open,
|
||||
};
|
||||
Stage m_stage = Stage::Closed;
|
||||
bool m_open{};
|
||||
ox::String m_path;
|
||||
ox::IString<255> m_str;
|
||||
|
||||
public:
|
||||
ox::Signal<ox::Error(ox::StringViewCR path)> newDir;
|
||||
|
||||
NewDir() noexcept;
|
||||
|
||||
void openPath(ox::StringViewCR path) noexcept;
|
||||
|
||||
void open() noexcept override;
|
||||
|
||||
void close() noexcept override;
|
||||
|
||||
[[nodiscard]]
|
||||
bool isOpen() const noexcept override;
|
||||
|
||||
void draw(StudioContext &ctx) noexcept override;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr ox::CStringView value() const noexcept {
|
||||
return m_str;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -14,7 +14,7 @@ namespace studio {
|
||||
|
||||
NewMenu::NewMenu() noexcept {
|
||||
setTitle("New Item");
|
||||
setSize({230, 140});
|
||||
setSize({280, 180});
|
||||
}
|
||||
|
||||
void NewMenu::open() noexcept {
|
||||
@ -22,6 +22,12 @@ void NewMenu::open() noexcept {
|
||||
m_selectedType = 0;
|
||||
m_itemName = "";
|
||||
m_typeName = "";
|
||||
m_path = "";
|
||||
}
|
||||
|
||||
void NewMenu::openPath(ox::StringParam path) noexcept {
|
||||
open();
|
||||
m_path = std::move(path);
|
||||
}
|
||||
|
||||
void NewMenu::close() noexcept {
|
||||
@ -33,7 +39,7 @@ bool NewMenu::isOpen() const noexcept {
|
||||
return m_open;
|
||||
}
|
||||
|
||||
void NewMenu::draw(studio::StudioContext &sctx) noexcept {
|
||||
void NewMenu::draw(StudioContext &sctx) noexcept {
|
||||
switch (m_stage) {
|
||||
case Stage::Opening:
|
||||
ImGui::OpenPopup(title().c_str());
|
||||
@ -46,51 +52,73 @@ void NewMenu::draw(studio::StudioContext &sctx) noexcept {
|
||||
case Stage::NewItemName:
|
||||
drawNewItemName(sctx);
|
||||
break;
|
||||
case Stage::NewItemTemplate:
|
||||
drawNewItemTemplate(sctx);
|
||||
break;
|
||||
case Stage::Closed:
|
||||
m_open = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void NewMenu::addItemMaker(ox::UniquePtr<studio::ItemMaker> &&im) noexcept {
|
||||
void NewMenu::addItemMaker(ox::UPtr<ItemMaker> &&im) noexcept {
|
||||
m_types.emplace_back(std::move(im));
|
||||
std::sort(
|
||||
m_types.begin(), m_types.end(),
|
||||
[](ox::UPtr<ItemMaker> const&im1, ox::UPtr<ItemMaker> const&im2) {
|
||||
return im1->typeName < im2->typeName;
|
||||
return im1->typeDisplayName() < im2->typeDisplayName();
|
||||
});
|
||||
}
|
||||
|
||||
void NewMenu::drawNewItemType(studio::StudioContext &sctx) noexcept {
|
||||
drawWindow(sctx.tctx, &m_open, [this] {
|
||||
auto const allocSz = m_types.size() * sizeof(char const*);
|
||||
auto mem = ox_malloca(allocSz, char const*, nullptr);
|
||||
auto items = ox::Span{mem.get(), allocSz};
|
||||
for (auto i = 0u; auto const&im : m_types) {
|
||||
items[i] = im->typeName.c_str();
|
||||
++i;
|
||||
void NewMenu::installItemTemplate(ox::UPtr<ItemTemplate> &tmplt) noexcept {
|
||||
for (auto const&im : m_types) {
|
||||
if (im->installTemplate(tmplt)) {
|
||||
break;
|
||||
}
|
||||
ImGui::ListBox("Item Type", &m_selectedType, items.data(), static_cast<int>(m_types.size()));
|
||||
drawFirstPageButtons();
|
||||
}
|
||||
}
|
||||
|
||||
void NewMenu::drawNewItemType(StudioContext const&sctx) noexcept {
|
||||
drawWindow(sctx.tctx, m_open, [this] {
|
||||
ig::ListBox("Item Type", [&](size_t const i) -> ox::CStringView {
|
||||
return m_types[i]->typeDisplayName();
|
||||
}, m_types.size(), m_selectedType, {200, 100});
|
||||
auto const&im = *m_types[m_selectedType];
|
||||
drawFirstPageButtons(im.itemTemplates().size() == 1 ?
|
||||
Stage::NewItemName : Stage::NewItemTemplate);
|
||||
});
|
||||
}
|
||||
|
||||
void NewMenu::drawNewItemName(studio::StudioContext &sctx) noexcept {
|
||||
drawWindow(sctx.tctx, &m_open, [this, &sctx] {
|
||||
auto const typeIdx = static_cast<std::size_t>(m_selectedType);
|
||||
if (typeIdx < m_types.size()) {
|
||||
void NewMenu::drawNewItemTemplate(StudioContext &sctx) noexcept {
|
||||
drawWindow(sctx.tctx, m_open, [this] {
|
||||
auto const&templates =
|
||||
m_types[m_selectedType]->itemTemplates();
|
||||
ig::ListBox("Template", [&](size_t const i) -> ox::CStringView {
|
||||
return templates[i]->name();
|
||||
}, templates.size(), m_selectedTemplate, {200, 100});
|
||||
drawButtons(Stage::NewItemType, Stage::NewItemName);
|
||||
});
|
||||
}
|
||||
|
||||
void NewMenu::drawNewItemName(StudioContext &sctx) noexcept {
|
||||
drawWindow(sctx.tctx, m_open, [this, &sctx] {
|
||||
if (m_selectedType < m_types.size()) {
|
||||
ig::InputText("Name", m_itemName);
|
||||
}
|
||||
drawLastPageButtons(sctx);
|
||||
});
|
||||
}
|
||||
|
||||
void NewMenu::drawFirstPageButtons() noexcept {
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 130);
|
||||
void NewMenu::drawButtons(Stage const prev, Stage const next) noexcept {
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 198);
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - 20);
|
||||
auto const btnSz = ImVec2(60, 20);
|
||||
constexpr ImVec2 btnSz{60, 20};
|
||||
if (ImGui::Button("Back", btnSz)) {
|
||||
m_stage = prev;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Next", btnSz)) {
|
||||
m_stage = Stage::NewItemName;
|
||||
m_stage = next;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel", btnSz)) {
|
||||
@ -99,35 +127,51 @@ void NewMenu::drawFirstPageButtons() noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
void NewMenu::drawLastPageButtons(studio::StudioContext &sctx) noexcept {
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 138);
|
||||
void NewMenu::drawFirstPageButtons(Stage const next) noexcept {
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 130);
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - 20);
|
||||
if (ImGui::Button("Back")) {
|
||||
m_stage = Stage::NewItemType;
|
||||
constexpr ImVec2 btnSz{60, 20};
|
||||
if (ImGui::Button("Next", btnSz)) {
|
||||
m_stage = next;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Finish")) {
|
||||
finish(sctx);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Quit")) {
|
||||
if (ImGui::Button("Cancel", btnSz)) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
m_stage = Stage::Closed;
|
||||
}
|
||||
}
|
||||
|
||||
void NewMenu::finish(studio::StudioContext &sctx) noexcept {
|
||||
void NewMenu::drawLastPageButtons(StudioContext &sctx) noexcept {
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 198);
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - 20);
|
||||
constexpr ImVec2 btnSz{60, 20};
|
||||
if (ImGui::Button("Back", btnSz)) {
|
||||
m_stage = Stage::NewItemType;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Finish", btnSz)) {
|
||||
finish(sctx);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Quit", btnSz)) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
m_stage = Stage::Closed;
|
||||
}
|
||||
}
|
||||
|
||||
void NewMenu::finish(StudioContext &sctx) noexcept {
|
||||
if (m_itemName.len() == 0) {
|
||||
oxLogError(ox::Error(1, "New file error: no file name"));
|
||||
oxLogError(ox::Error{1, "New file error: no file name"});
|
||||
return;
|
||||
}
|
||||
auto const&typeMaker = *m_types[static_cast<std::size_t>(m_selectedType)];
|
||||
if (sctx.project->exists(typeMaker.itemPath(m_itemName))) {
|
||||
oxLogError(ox::Error(1, "New file error: file already exists"));
|
||||
auto const&im = *m_types[m_selectedType];
|
||||
auto const path = m_path.len() ?
|
||||
im.itemPath(m_itemName, m_path) : im.itemPath(m_itemName);
|
||||
if (sctx.project->exists(path)) {
|
||||
oxLogError(ox::Error{1, "New file error: file already exists"});
|
||||
return;
|
||||
}
|
||||
auto const [path, err] = typeMaker.write(sctx, m_itemName);
|
||||
if (err) {
|
||||
if (auto const err = im.write(sctx, path, m_selectedTemplate)) {
|
||||
oxLogError(err);
|
||||
return;
|
||||
}
|
||||
|
@ -13,13 +13,14 @@
|
||||
|
||||
namespace studio {
|
||||
|
||||
class NewMenu: public studio::Popup {
|
||||
class NewMenu final: public Popup {
|
||||
public:
|
||||
enum class Stage {
|
||||
Closed,
|
||||
Opening,
|
||||
NewItemType,
|
||||
NewItemName,
|
||||
NewItemTemplate,
|
||||
};
|
||||
|
||||
// emits path parameter
|
||||
@ -29,13 +30,17 @@ class NewMenu: public studio::Popup {
|
||||
Stage m_stage = Stage::Closed;
|
||||
ox::String m_typeName;
|
||||
ox::IString<255> m_itemName;
|
||||
ox::Vector<ox::UniquePtr<studio::ItemMaker>> m_types;
|
||||
int m_selectedType = 0;
|
||||
ox::String m_path;
|
||||
ox::Vector<ox::UPtr<studio::ItemMaker>> m_types;
|
||||
size_t m_selectedType = 0;
|
||||
size_t m_selectedTemplate = 0;
|
||||
bool m_open = false;
|
||||
|
||||
public:
|
||||
NewMenu() noexcept;
|
||||
|
||||
void openPath(ox::StringParam path) noexcept;
|
||||
|
||||
void open() noexcept override;
|
||||
|
||||
void close() noexcept override;
|
||||
@ -43,37 +48,70 @@ class NewMenu: public studio::Popup {
|
||||
[[nodiscard]]
|
||||
bool isOpen() const noexcept override;
|
||||
|
||||
void draw(studio::StudioContext &sctx) noexcept override;
|
||||
void draw(StudioContext &sctx) noexcept override;
|
||||
|
||||
template<typename T>
|
||||
void addItemType(ox::String name, ox::String parentDir, ox::String fileExt, T itemTempl, ox::ClawFormat pFmt = ox::ClawFormat::Metal) noexcept;
|
||||
void addItemType(
|
||||
ox::StringParam displayName,
|
||||
ox::StringParam parentDir,
|
||||
ox::StringParam fileExt,
|
||||
T itemTempl,
|
||||
ox::ClawFormat pFmt = ox::ClawFormat::Metal) noexcept;
|
||||
|
||||
template<typename T>
|
||||
void addItemType(ox::String name, ox::String parentDir, ox::String fileExt, ox::ClawFormat pFmt = ox::ClawFormat::Metal) noexcept;
|
||||
void addItemType(
|
||||
ox::StringParam displayName,
|
||||
ox::StringParam parentDir,
|
||||
ox::StringParam fileExt,
|
||||
ox::ClawFormat pFmt = ox::ClawFormat::Metal) noexcept;
|
||||
|
||||
void addItemMaker(ox::UniquePtr<studio::ItemMaker> &&im) noexcept;
|
||||
void addItemMaker(ox::UPtr<ItemMaker> &&im) noexcept;
|
||||
|
||||
void installItemTemplate(ox::UPtr<ItemTemplate> &tmplt) noexcept;
|
||||
|
||||
private:
|
||||
void drawNewItemType(studio::StudioContext &sctx) noexcept;
|
||||
void drawNewItemType(StudioContext const&sctx) noexcept;
|
||||
|
||||
void drawNewItemName(studio::StudioContext &sctx) noexcept;
|
||||
void drawNewItemName(StudioContext &sctx) noexcept;
|
||||
|
||||
void drawFirstPageButtons() noexcept;
|
||||
void drawNewItemTemplate(StudioContext &sctx) noexcept;
|
||||
|
||||
void drawLastPageButtons(studio::StudioContext &sctx) noexcept;
|
||||
void drawButtons(Stage prev, Stage next) noexcept;
|
||||
|
||||
void finish(studio::StudioContext &sctx) noexcept;
|
||||
void drawFirstPageButtons(Stage next) noexcept;
|
||||
|
||||
void drawLastPageButtons(StudioContext &sctx) noexcept;
|
||||
|
||||
void finish(StudioContext &sctx) noexcept;
|
||||
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
void NewMenu::addItemType(ox::String displayName, ox::String parentDir, ox::String fileExt, T itemTempl, ox::ClawFormat pFmt) noexcept {
|
||||
m_types.emplace_back(ox::make<studio::ItemMakerT<T>>(std::move(displayName), std::move(parentDir), std::move(fileExt), std::move(itemTempl), pFmt));
|
||||
void NewMenu::addItemType(
|
||||
ox::StringParam displayName,
|
||||
ox::StringParam parentDir,
|
||||
ox::StringParam fileExt,
|
||||
T itemTempl,
|
||||
ox::ClawFormat const pFmt) noexcept {
|
||||
m_types.emplace_back(ox::make<ItemMakerT<T>>(
|
||||
std::move(displayName),
|
||||
std::move(parentDir),
|
||||
std::move(fileExt),
|
||||
std::move(itemTempl),
|
||||
pFmt));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void NewMenu::addItemType(ox::String displayName, ox::String parentDir, ox::String fileExt, ox::ClawFormat pFmt) noexcept {
|
||||
m_types.emplace_back(ox::make<studio::ItemMakerT<T>>(std::move(displayName), std::move(parentDir), std::move(fileExt), pFmt));
|
||||
void NewMenu::addItemType(
|
||||
ox::StringParam displayName,
|
||||
ox::StringParam parentDir,
|
||||
ox::StringParam fileExt,
|
||||
ox::ClawFormat const pFmt) noexcept {
|
||||
m_types.emplace_back(ox::make<ItemMakerT<T>>(
|
||||
std::move(displayName),
|
||||
std::move(parentDir),
|
||||
std::move(fileExt),
|
||||
pFmt));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ void NewProject::draw(studio::StudioContext &ctx) noexcept {
|
||||
}
|
||||
|
||||
void NewProject::drawNewProjectName(studio::StudioContext &sctx) noexcept {
|
||||
drawWindow(sctx.tctx, &m_open, [this, &sctx] {
|
||||
drawWindow(sctx.tctx, m_open, [this, &sctx] {
|
||||
ig::InputText("Name", m_projectName);
|
||||
ImGui::Text("Path: %s", m_projectPath.c_str());
|
||||
if (ImGui::Button("Browse")) {
|
||||
|
@ -12,12 +12,12 @@ namespace studio {
|
||||
|
||||
static ox::Result<ox::UniquePtr<ProjectTreeModel>> buildProjectTreeModel(
|
||||
ProjectExplorer &explorer,
|
||||
ox::StringView name,
|
||||
ox::StringParam name,
|
||||
ox::StringView path,
|
||||
ProjectTreeModel *parent) noexcept {
|
||||
auto const fs = explorer.romFs();
|
||||
OX_REQUIRE(stat, fs->stat(path));
|
||||
auto out = ox::make_unique<ProjectTreeModel>(explorer, ox::String(name), parent);
|
||||
auto out = ox::make_unique<ProjectTreeModel>(explorer, std::move(name), parent);
|
||||
if (stat.fileType == ox::FileType::Directory) {
|
||||
OX_REQUIRE_M(children, fs->ls(path));
|
||||
std::sort(children.begin(), children.end());
|
||||
@ -37,7 +37,7 @@ static ox::Result<ox::UniquePtr<ProjectTreeModel>> buildProjectTreeModel(
|
||||
ProjectExplorer::ProjectExplorer(turbine::Context &ctx) noexcept: m_ctx(ctx) {
|
||||
}
|
||||
|
||||
void ProjectExplorer::draw(studio::StudioContext &ctx) noexcept {
|
||||
void ProjectExplorer::draw(StudioContext &ctx) noexcept {
|
||||
auto const viewport = ImGui::GetContentRegionAvail();
|
||||
ImGui::BeginChild("ProjectExplorer", ImVec2(300, viewport.y), true);
|
||||
ImGui::SetNextItemOpen(true);
|
||||
@ -54,7 +54,7 @@ void ProjectExplorer::setModel(ox::UPtr<ProjectTreeModel> &&model) noexcept {
|
||||
ox::Error ProjectExplorer::refreshProjectTreeModel(ox::StringViewCR) noexcept {
|
||||
OX_REQUIRE_M(model, buildProjectTreeModel(*this, "Project", "/", nullptr));
|
||||
setModel(std::move(model));
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -12,27 +12,31 @@
|
||||
|
||||
namespace studio {
|
||||
|
||||
class ProjectExplorer: public studio::Widget {
|
||||
class ProjectExplorer: public Widget {
|
||||
private:
|
||||
ox::UPtr<ProjectTreeModel> m_treeModel;
|
||||
turbine::Context &m_ctx;
|
||||
|
||||
public:
|
||||
// slots
|
||||
ox::Signal<ox::Error(ox::StringViewCR)> fileChosen;
|
||||
ox::Signal<ox::Error(ox::StringViewCR)> addItem;
|
||||
ox::Signal<ox::Error(ox::StringViewCR)> addDir;
|
||||
ox::Signal<ox::Error(ox::StringViewCR)> deleteItem;
|
||||
|
||||
explicit ProjectExplorer(turbine::Context &ctx) noexcept;
|
||||
|
||||
void draw(studio::StudioContext &ctx) noexcept override;
|
||||
void draw(StudioContext &ctx) noexcept override;
|
||||
|
||||
void setModel(ox::UPtr<ProjectTreeModel> &&model) noexcept;
|
||||
|
||||
ox::Error refreshProjectTreeModel(ox::StringViewCR = {}) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
inline ox::FileSystem *romFs() noexcept {
|
||||
ox::FileSystem *romFs() noexcept {
|
||||
return rom(m_ctx);
|
||||
}
|
||||
|
||||
// slots
|
||||
public:
|
||||
ox::Signal<ox::Error(ox::StringView const&)> fileChosen;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -4,13 +4,18 @@
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include <studio/dragdrop.hpp>
|
||||
#include <studio/imguiutil.hpp>
|
||||
|
||||
#include "projectexplorer.hpp"
|
||||
#include "projecttreemodel.hpp"
|
||||
|
||||
namespace studio {
|
||||
|
||||
ProjectTreeModel::ProjectTreeModel(ProjectExplorer &explorer, ox::String name,
|
||||
ProjectTreeModel *parent) noexcept:
|
||||
ProjectTreeModel::ProjectTreeModel(
|
||||
ProjectExplorer &explorer,
|
||||
ox::StringParam name,
|
||||
ProjectTreeModel *parent) noexcept:
|
||||
m_explorer(explorer),
|
||||
m_parent(parent),
|
||||
m_name(std::move(name)) {
|
||||
@ -23,14 +28,18 @@ ProjectTreeModel::ProjectTreeModel(ProjectTreeModel &&other) noexcept:
|
||||
m_children(std::move(other.m_children)) {
|
||||
}
|
||||
|
||||
void ProjectTreeModel::draw(turbine::Context &ctx) const noexcept {
|
||||
void ProjectTreeModel::draw(turbine::Context &tctx) const noexcept {
|
||||
constexpr auto dirFlags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
|
||||
if (!m_children.empty()) {
|
||||
if (ImGui::TreeNodeEx(m_name.c_str(), dirFlags)) {
|
||||
drawDirContextMenu();
|
||||
for (auto const&child : m_children) {
|
||||
child->draw(ctx);
|
||||
child->draw(tctx);
|
||||
}
|
||||
ImGui::TreePop();
|
||||
} else {
|
||||
ig::IDStackItem const idStackItem{m_name};
|
||||
drawDirContextMenu();
|
||||
}
|
||||
} else {
|
||||
auto const path = fullPath();
|
||||
@ -39,7 +48,17 @@ void ProjectTreeModel::draw(turbine::Context &ctx) const noexcept {
|
||||
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0)) {
|
||||
m_explorer.fileChosen.emit(path);
|
||||
}
|
||||
if (ImGui::BeginPopupContextItem("FileMenu", ImGuiPopupFlags_MouseButtonRight)) {
|
||||
if (ImGui::MenuItem("Delete")) {
|
||||
m_explorer.deleteItem.emit(path);
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
ImGui::TreePop();
|
||||
std::ignore = ig::dragDropSource([this] {
|
||||
ImGui::Text("%s", m_name.c_str());
|
||||
return ig::setDragDropPayload("FileRef", FileRef{fullPath<ox::String>()});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -48,11 +67,19 @@ void ProjectTreeModel::setChildren(ox::Vector<ox::UPtr<ProjectTreeModel>> childr
|
||||
m_children = std::move(children);
|
||||
}
|
||||
|
||||
ox::BasicString<255> ProjectTreeModel::fullPath() const noexcept {
|
||||
if (m_parent) {
|
||||
return m_parent->fullPath() + "/" + ox::StringView(m_name);
|
||||
}
|
||||
return {};
|
||||
void ProjectTreeModel::drawDirContextMenu() const noexcept {
|
||||
if (ImGui::BeginPopupContextItem("DirMenu", ImGuiPopupFlags_MouseButtonRight)) {
|
||||
if (ImGui::MenuItem("Add Item")) {
|
||||
m_explorer.addItem.emit(fullPath());
|
||||
}
|
||||
if (ImGui::MenuItem("Add Directory")) {
|
||||
m_explorer.addDir.emit(fullPath());
|
||||
}
|
||||
if (ImGui::MenuItem("Delete")) {
|
||||
m_explorer.deleteItem.emit(fullPath());
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,19 +17,30 @@ class ProjectTreeModel {
|
||||
ProjectTreeModel *m_parent = nullptr;
|
||||
ox::String m_name;
|
||||
ox::Vector<ox::UPtr<ProjectTreeModel>> m_children;
|
||||
|
||||
public:
|
||||
explicit ProjectTreeModel(class ProjectExplorer &explorer, ox::String name,
|
||||
ProjectTreeModel *parent = nullptr) noexcept;
|
||||
explicit ProjectTreeModel(
|
||||
ProjectExplorer &explorer, ox::StringParam name,
|
||||
ProjectTreeModel *parent = nullptr) noexcept;
|
||||
|
||||
ProjectTreeModel(ProjectTreeModel &&other) noexcept;
|
||||
|
||||
void draw(turbine::Context &ctx) const noexcept;
|
||||
void draw(turbine::Context &tctx) const noexcept;
|
||||
|
||||
void setChildren(ox::Vector<ox::UPtr<ProjectTreeModel>> children) noexcept;
|
||||
|
||||
private:
|
||||
void drawDirContextMenu() const noexcept;
|
||||
|
||||
template<typename Str = ox::BasicString<255>>
|
||||
[[nodiscard]]
|
||||
ox::BasicString<255> fullPath() const noexcept;
|
||||
Str fullPath() const noexcept {
|
||||
if (m_parent) {
|
||||
return m_parent->fullPath<Str>() + "/" + m_name;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -52,6 +52,9 @@ StudioUI::StudioUI(turbine::Context &ctx, ox::StringParam projectDataDir) noexce
|
||||
m_aboutPopup(m_tctx) {
|
||||
turbine::setApplicationData(m_tctx, &m_sctx);
|
||||
m_projectExplorer.fileChosen.connect(this, &StudioUI::openFile);
|
||||
m_projectExplorer.addDir.connect(this, &StudioUI::addDir);
|
||||
m_projectExplorer.addItem.connect(this, &StudioUI::addFile);
|
||||
m_projectExplorer.deleteItem.connect(this, &StudioUI::deleteFile);
|
||||
m_newProject.finished.connect(this, &StudioUI::createOpenProject);
|
||||
m_newMenu.finished.connect(this, &StudioUI::openFile);
|
||||
ImGui::GetIO().IniFilename = nullptr;
|
||||
@ -251,6 +254,10 @@ void StudioUI::loadModule(Module const&mod) noexcept {
|
||||
for (auto &im : mod.itemMakers(m_sctx)) {
|
||||
m_newMenu.addItemMaker(std::move(im));
|
||||
}
|
||||
auto tmplts = mod.itemTemplates(m_sctx);
|
||||
for (auto &t : tmplts) {
|
||||
m_newMenu.installItemTemplate(t);
|
||||
}
|
||||
}
|
||||
|
||||
void StudioUI::loadModules() noexcept {
|
||||
@ -338,6 +345,21 @@ void StudioUI::handleKeyInput() noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
ox::Error StudioUI::addDir(ox::StringViewCR path) noexcept {
|
||||
m_newDirDialog.openPath(path);
|
||||
return {};
|
||||
}
|
||||
|
||||
ox::Error StudioUI::addFile(ox::StringViewCR path) noexcept {
|
||||
m_newMenu.openPath(path);
|
||||
return {};
|
||||
}
|
||||
|
||||
ox::Error StudioUI::deleteFile(ox::StringViewCR path) noexcept {
|
||||
m_deleteConfirmation.openPath(path);
|
||||
return {};
|
||||
}
|
||||
|
||||
ox::Error StudioUI::createOpenProject(ox::StringViewCR path) noexcept {
|
||||
std::error_code ec;
|
||||
std::filesystem::create_directories(toStdStringView(path), ec);
|
||||
@ -352,9 +374,11 @@ ox::Error StudioUI::openProjectPath(ox::StringParam path) noexcept {
|
||||
OX_RETURN_ERROR(
|
||||
ox::make_unique_catch<Project>(keelCtx(m_tctx), std::move(path), m_projectDataDir)
|
||||
.moveTo(m_project));
|
||||
auto const sctx = applicationData<StudioContext>(m_tctx);
|
||||
sctx->project = m_project.get();
|
||||
m_sctx.project = m_project.get();
|
||||
turbine::setWindowTitle(m_tctx, ox::sfmt("{} - {}", keelCtx(m_tctx).appName, m_project->projectPath()));
|
||||
m_deleteConfirmation.deleteFile.connect(m_sctx.project, &Project::deleteItem);
|
||||
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->fileDeleted.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel);
|
||||
m_openFiles.clear();
|
||||
|
@ -12,7 +12,10 @@
|
||||
#include <studio/module.hpp>
|
||||
#include <studio/project.hpp>
|
||||
#include <studio/task.hpp>
|
||||
|
||||
#include "aboutpopup.hpp"
|
||||
#include "deleteconfirmation.hpp"
|
||||
#include "newdir.hpp"
|
||||
#include "newmenu.hpp"
|
||||
#include "newproject.hpp"
|
||||
#include "projectexplorer.hpp"
|
||||
@ -38,12 +41,16 @@ class StudioUI: public ox::SignalHandler {
|
||||
BaseEditor *m_activeEditor = nullptr;
|
||||
BaseEditor *m_activeEditorUpdatePending = nullptr;
|
||||
NewMenu m_newMenu;
|
||||
DeleteConfirmation m_deleteConfirmation;
|
||||
NewDir m_newDirDialog;
|
||||
NewProject m_newProject;
|
||||
AboutPopup m_aboutPopup;
|
||||
ox::Array<Popup*, 3> const m_popups = {
|
||||
ox::Array<Popup*, 5> const m_popups = {
|
||||
&m_newMenu,
|
||||
&m_newProject,
|
||||
&m_aboutPopup
|
||||
&m_aboutPopup,
|
||||
&m_deleteConfirmation,
|
||||
&m_newDirDialog,
|
||||
};
|
||||
bool m_showProjectExplorer = true;
|
||||
|
||||
@ -83,6 +90,12 @@ class StudioUI: public ox::SignalHandler {
|
||||
|
||||
void handleKeyInput() noexcept;
|
||||
|
||||
ox::Error addDir(ox::StringViewCR path) noexcept;
|
||||
|
||||
ox::Error addFile(ox::StringViewCR path) noexcept;
|
||||
|
||||
ox::Error deleteFile(ox::StringViewCR path) noexcept;
|
||||
|
||||
ox::Error createOpenProject(ox::StringViewCR path) noexcept;
|
||||
|
||||
ox::Error openProjectPath(ox::StringParam path) noexcept;
|
||||
|
22
src/olympic/studio/modlib/include/studio/dragdrop.hpp
Normal file
22
src/olympic/studio/modlib/include/studio/dragdrop.hpp
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/string.hpp>
|
||||
#include <ox/model/def.hpp>
|
||||
|
||||
namespace studio {
|
||||
|
||||
struct FileRef {
|
||||
static constexpr auto TypeName = "net.drinkingtea.studio.FileRef";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
ox::String path;
|
||||
};
|
||||
|
||||
OX_MODEL_BEGIN(FileRef)
|
||||
OX_MODEL_FIELD(path)
|
||||
OX_MODEL_END()
|
||||
|
||||
}
|
@ -63,6 +63,7 @@ auto dragDropSource(auto const&cb) noexcept {
|
||||
if (ig::DragDropSource const tgt; tgt) [[unlikely]] {
|
||||
return cb();
|
||||
}
|
||||
return ox::Error{};
|
||||
} else {
|
||||
if (ig::DragDropSource const tgt; tgt) [[unlikely]] {
|
||||
cb();
|
||||
@ -175,9 +176,16 @@ enum class PopupResponse {
|
||||
Cancel,
|
||||
};
|
||||
|
||||
PopupResponse PopupControlsOkCancel(float popupWidth, bool &popupOpen);
|
||||
PopupResponse PopupControlsOkCancel(
|
||||
float popupWidth,
|
||||
bool &popupOpen,
|
||||
ox::CStringViewCR ok = "OK",
|
||||
ox::CStringViewCR cancel = "Cancel");
|
||||
|
||||
PopupResponse PopupControlsOkCancel(bool &popupOpen);
|
||||
PopupResponse PopupControlsOkCancel(
|
||||
bool &popupOpen,
|
||||
ox::CStringViewCR ok = "OK",
|
||||
ox::CStringViewCR cancel = "Cancel");
|
||||
|
||||
[[nodiscard]]
|
||||
bool BeginPopup(turbine::Context &ctx, ox::CStringViewCR popupName, bool &show, ImVec2 const&sz = {285, 0});
|
||||
@ -214,7 +222,8 @@ bool ListBox(
|
||||
ox::CStringViewCR name,
|
||||
std::function<ox::CStringView(size_t)> const&f,
|
||||
size_t strCnt,
|
||||
size_t &selIdx) noexcept;
|
||||
size_t &selIdx,
|
||||
ImVec2 const&sz = {0, 0}) noexcept;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -4,7 +4,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/claw/claw.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
#include <ox/model/typenamecatcher.hpp>
|
||||
|
||||
#include <keel/media.hpp>
|
||||
#include <turbine/context.hpp>
|
||||
@ -13,75 +15,249 @@
|
||||
|
||||
namespace studio {
|
||||
|
||||
class ItemMaker {
|
||||
class ItemTemplate {
|
||||
private:
|
||||
ox::String const m_name{"Default"};
|
||||
|
||||
public:
|
||||
ox::String const typeName;
|
||||
ox::String const parentDir;
|
||||
ox::String const fileExt;
|
||||
constexpr explicit ItemMaker(
|
||||
explicit ItemTemplate() noexcept = default;
|
||||
|
||||
explicit ItemTemplate(ox::StringParam name) noexcept: m_name{std::move(name)} {}
|
||||
|
||||
virtual ~ItemTemplate() = default;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr ox::String const&name() const noexcept {
|
||||
return m_name;
|
||||
}
|
||||
|
||||
constexpr bool operator<=>(ItemTemplate const&other) const noexcept {
|
||||
return m_name != other.name() ? (m_name < other.name() ? -1 : 1) : 0;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
ox::CStringView displayName() const noexcept {
|
||||
return m_name;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
virtual ox::CStringView typeName() const noexcept = 0;
|
||||
|
||||
[[nodiscard]]
|
||||
virtual int typeVersion() const noexcept = 0;
|
||||
|
||||
virtual ox::Result<ox::UAnyPtr> getItem(
|
||||
keel::Context &ctx, ox::StringViewCR name, int version) noexcept = 0;
|
||||
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class ItemTemplateT: public ItemTemplate {
|
||||
private:
|
||||
T const m_item;
|
||||
|
||||
public:
|
||||
constexpr ItemTemplateT() noexcept = default;
|
||||
|
||||
explicit ItemTemplateT(ox::StringParam name, T item) noexcept:
|
||||
ItemTemplate{std::move(name)}, m_item{std::move(item)} {}
|
||||
|
||||
[[nodiscard]]
|
||||
ox::CStringView typeName() const noexcept final {
|
||||
return ox::ModelTypeName_v<T>;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
int typeVersion() const noexcept final {
|
||||
return ox::ModelTypeVersion_v<T>;
|
||||
}
|
||||
|
||||
ox::Result<ox::UAnyPtr> getItem(
|
||||
keel::Context &kctx, ox::StringViewCR name, int const version) noexcept final {
|
||||
if (ox::ModelTypeVersion_v<T> != version || ox::ModelTypeName_v<T> != name) {
|
||||
OX_REQUIRE_M(item, keel::convert(kctx, m_item, name, version));
|
||||
auto out = ox::Result<ox::UAnyPtr>{item->moveToCopy()};
|
||||
ox::safeDelete(item.release());
|
||||
return out;
|
||||
}
|
||||
return ox::UAnyPtr{new T{m_item}};
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class ItemMaker {
|
||||
private:
|
||||
ox::Vector<ox::UPtr<ItemTemplate>> m_templates;
|
||||
ox::String const m_parentDir;
|
||||
ox::String const m_fileExt;
|
||||
ox::String const m_typeDisplayName;
|
||||
|
||||
public:
|
||||
constexpr ItemMaker(
|
||||
ox::StringParam pName,
|
||||
ox::StringParam pParentDir,
|
||||
ox::StringParam pFileExt) noexcept:
|
||||
typeName{std::move(pName)},
|
||||
parentDir{std::move(pParentDir)},
|
||||
fileExt{std::move(pFileExt)} {
|
||||
m_parentDir{std::move(pParentDir)},
|
||||
m_fileExt{std::move(pFileExt)},
|
||||
m_typeDisplayName{std::move(pName)} {
|
||||
}
|
||||
|
||||
virtual ~ItemMaker() noexcept = default;
|
||||
|
||||
[[nodiscard]]
|
||||
virtual ox::String itemPath(ox::StringView pName) const noexcept {
|
||||
return ox::sfmt("/{}/{}.{}", parentDir, pName, fileExt);
|
||||
ox::String const&typeDisplayName() const noexcept {
|
||||
return m_typeDisplayName;
|
||||
}
|
||||
|
||||
bool installTemplate(ox::UPtr<ItemTemplate> &tmpl) {
|
||||
if (typeName() == tmpl->typeName() &&
|
||||
typeVersion() <= tmpl->typeVersion()) {
|
||||
m_templates.emplace_back(std::move(tmpl));
|
||||
// begin() + 1 because 'Default' should always be first
|
||||
std::sort(m_templates.begin() + 1, m_templates.end());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool installTemplate(ox::UPtr<ItemTemplate> &&tmpl) {
|
||||
return installTemplate(tmpl);
|
||||
}
|
||||
|
||||
constexpr ox::Vector<ox::UPtr<ItemTemplate>> const&itemTemplates() const noexcept {
|
||||
return m_templates;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
ox::String const&defaultPath() const noexcept {
|
||||
return m_parentDir;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
ox::String itemPath(ox::StringViewCR pName, ox::StringViewCR pPath) const noexcept {
|
||||
return ox::sfmt("/{}/{}.{}", pPath, pName, m_fileExt);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
ox::String itemPath(ox::StringViewCR pName) const noexcept {
|
||||
return ox::sfmt("/{}/{}.{}", m_parentDir, pName, m_fileExt);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
virtual ox::StringView typeName() const noexcept = 0;
|
||||
|
||||
[[nodiscard]]
|
||||
virtual int typeVersion() const noexcept = 0;
|
||||
|
||||
/**
|
||||
* Returns path of the file created.
|
||||
* @param ctx
|
||||
* @param pPath
|
||||
* @param pTemplateIdx
|
||||
* @return path of file or error in Result
|
||||
*/
|
||||
ox::Error write(
|
||||
StudioContext &ctx,
|
||||
ox::StringViewCR pPath,
|
||||
size_t pTemplateIdx) const noexcept {
|
||||
return writeItem(ctx, pPath, pTemplateIdx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns path of the file created.
|
||||
* @param ctx
|
||||
* @param pPath
|
||||
* @param pName
|
||||
* @param pTemplateIdx
|
||||
* @return path of file or error in Result
|
||||
*/
|
||||
virtual ox::Result<ox::String> write(StudioContext &ctx, ox::StringView pName) const noexcept = 0;
|
||||
ox::Error write(
|
||||
StudioContext &ctx,
|
||||
ox::StringViewCR pPath,
|
||||
ox::StringViewCR pName,
|
||||
size_t pTemplateIdx) const noexcept {
|
||||
auto const path = itemPath(pName, pPath);
|
||||
return writeItem(ctx, path, pTemplateIdx);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ox::Error writeItem(
|
||||
StudioContext &ctx,
|
||||
ox::StringViewCR pPath,
|
||||
size_t pTemplateIdx) const noexcept = 0;
|
||||
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class ItemMakerT: public ItemMaker {
|
||||
class ItemMakerT final: public ItemMaker {
|
||||
private:
|
||||
T const m_item;
|
||||
ox::ClawFormat const m_fmt;
|
||||
|
||||
public:
|
||||
constexpr ItemMakerT(
|
||||
ox::StringParam pDisplayName,
|
||||
ox::StringParam pParentDir,
|
||||
ox::StringParam fileExt,
|
||||
ox::ClawFormat pFmt = ox::ClawFormat::Metal) noexcept:
|
||||
ItemMaker(std::move(pDisplayName), std::move(pParentDir), std::move(fileExt)),
|
||||
m_fmt(pFmt) {
|
||||
ox::ClawFormat const pFmt = ox::ClawFormat::Metal) noexcept:
|
||||
ItemMaker(
|
||||
std::move(pDisplayName),
|
||||
std::move(pParentDir),
|
||||
std::move(fileExt)),
|
||||
m_fmt{pFmt} {
|
||||
installTemplate(ox::make_unique<ItemTemplateT<T>>());
|
||||
}
|
||||
|
||||
constexpr ItemMakerT(
|
||||
ox::StringParam pDisplayName,
|
||||
ox::StringParam pParentDir,
|
||||
ox::StringParam fileExt,
|
||||
T pItem,
|
||||
ox::ClawFormat pFmt) noexcept:
|
||||
ItemMaker(std::move(pDisplayName), std::move(pParentDir), std::move(fileExt)),
|
||||
m_item(std::move(pItem)),
|
||||
m_fmt(pFmt) {
|
||||
T const&pItem,
|
||||
ox::ClawFormat const pFmt) noexcept:
|
||||
ItemMaker(
|
||||
std::move(pDisplayName),
|
||||
std::move(pParentDir),
|
||||
std::move(fileExt)),
|
||||
m_fmt{pFmt} {
|
||||
installTemplate(ox::make_unique<ItemTemplateT<T>>(std::move(pItem)));
|
||||
}
|
||||
|
||||
constexpr ItemMakerT(
|
||||
ox::StringParam pDisplayName,
|
||||
ox::StringParam pParentDir,
|
||||
ox::StringParam fileExt,
|
||||
T &&pItem,
|
||||
ox::ClawFormat pFmt) noexcept:
|
||||
ItemMaker(std::move(pDisplayName), std::move(pParentDir), std::move(fileExt)),
|
||||
m_item(std::move(pItem)),
|
||||
m_fmt(pFmt) {
|
||||
ox::ClawFormat const pFmt) noexcept:
|
||||
ItemMaker(
|
||||
std::move(pDisplayName),
|
||||
std::move(pParentDir),
|
||||
std::move(fileExt)),
|
||||
m_fmt{pFmt} {
|
||||
installTemplate(ox::make_unique<ItemTemplateT<T>>(std::move(pItem)));
|
||||
}
|
||||
ox::Result<ox::String> write(studio::StudioContext &sctx, ox::StringView const pName) const noexcept override {
|
||||
auto const path = itemPath(pName);
|
||||
createUuidMapping(keelCtx(sctx.tctx), path, ox::UUID::generate().unwrap());
|
||||
OX_RETURN_ERROR(sctx.project->writeObj(path, m_item, m_fmt));
|
||||
return path;
|
||||
|
||||
ox::StringView typeName() const noexcept override {
|
||||
return ox::ModelTypeName_v<T>;
|
||||
}
|
||||
|
||||
int typeVersion() const noexcept override {
|
||||
return ox::ModelTypeVersion_v<T>;
|
||||
}
|
||||
|
||||
ox::Error writeItem(
|
||||
StudioContext &ctx,
|
||||
ox::StringViewCR pPath,
|
||||
size_t const pTemplateIdx) const noexcept override {
|
||||
createUuidMapping(keelCtx(ctx.tctx), pPath, ox::UUID::generate().unwrap());
|
||||
auto const&templates = itemTemplates();
|
||||
auto const tmplIdx = pTemplateIdx < templates.size() ? pTemplateIdx : 0;
|
||||
OX_REQUIRE_M(tmpl, templates[tmplIdx]->getItem(
|
||||
keelCtx(ctx), typeName(), typeVersion()));
|
||||
auto item = tmpl.template get<T>();
|
||||
OX_RETURN_ERROR(ctx.project->writeObj(pPath, *item, m_fmt));
|
||||
tmpl.free();
|
||||
return {};
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -31,6 +31,8 @@ class Module {
|
||||
|
||||
virtual ox::Vector<ox::UPtr<ItemMaker>> itemMakers(studio::StudioContext&) const;
|
||||
|
||||
virtual ox::Vector<ox::UPtr<ItemTemplate>> itemTemplates(studio::StudioContext&) const;
|
||||
|
||||
};
|
||||
|
||||
template<typename Editor>
|
||||
|
@ -47,7 +47,7 @@ class Popup {
|
||||
return m_title;
|
||||
}
|
||||
|
||||
void drawWindow(turbine::Context &ctx, bool *open, std::function<void()> const&drawContents);
|
||||
void drawWindow(turbine::Context &ctx, bool &open, std::function<void()> const&drawContents);
|
||||
|
||||
};
|
||||
|
||||
|
@ -19,6 +19,7 @@ namespace studio {
|
||||
|
||||
enum class ProjectEvent {
|
||||
None,
|
||||
DirAdded,
|
||||
FileAdded,
|
||||
// FileRecognized is triggered for all matching files upon a new
|
||||
// subscription to a section of the project and upon the addition of a file.
|
||||
@ -45,7 +46,7 @@ constexpr ox::StringView parentDir(ox::StringView path) noexcept {
|
||||
return substr(path, 0, extStart);
|
||||
}
|
||||
|
||||
class Project {
|
||||
class Project: public ox::SignalHandler {
|
||||
private:
|
||||
ox::SmallMap<ox::String, ox::Optional<ox::ClawFormat>> m_typeFmt;
|
||||
keel::Context &m_ctx;
|
||||
@ -91,6 +92,8 @@ class Project {
|
||||
|
||||
ox::Result<ox::FileStat> stat(ox::StringViewCR path) const noexcept;
|
||||
|
||||
ox::Error deleteItem(ox::StringViewCR path) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
bool exists(ox::StringViewCR path) const noexcept;
|
||||
|
||||
@ -118,6 +121,7 @@ class Project {
|
||||
// signals
|
||||
public:
|
||||
ox::Signal<ox::Error(ProjectEvent, ox::StringViewCR)> fileEvent;
|
||||
ox::Signal<ox::Error(ox::StringViewCR)> dirAdded;
|
||||
ox::Signal<ox::Error(ox::StringViewCR)> fileAdded;
|
||||
// FileRecognized is triggered for all matching files upon a new
|
||||
// subscription to a section of the project and upon the addition of a
|
||||
@ -175,6 +179,9 @@ ox::Error Project::subscribe(ProjectEvent e, ox::SignalHandler *tgt, Functor &&s
|
||||
switch (e) {
|
||||
case ProjectEvent::None:
|
||||
break;
|
||||
case ProjectEvent::DirAdded:
|
||||
connect(this, &Project::dirAdded, tgt, slot);
|
||||
break;
|
||||
case ProjectEvent::FileAdded:
|
||||
connect(this, &Project::fileAdded, tgt, slot);
|
||||
break;
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include <studio/configio.hpp>
|
||||
#include <studio/context.hpp>
|
||||
#include <studio/dragdrop.hpp>
|
||||
#include <studio/editor.hpp>
|
||||
#include <studio/filedialog.hpp>
|
||||
#include <studio/imguiutil.hpp>
|
||||
|
@ -54,18 +54,22 @@ bool PushButton(ox::CStringViewCR lbl, ImVec2 const&btnSz) noexcept {
|
||||
return ImGui::Button(lbl.c_str(), btnSz);
|
||||
}
|
||||
|
||||
PopupResponse PopupControlsOkCancel(float popupWidth, bool &popupOpen) {
|
||||
PopupResponse PopupControlsOkCancel(
|
||||
float popupWidth,
|
||||
bool &popupOpen,
|
||||
ox::CStringViewCR ok,
|
||||
ox::CStringViewCR cancel) {
|
||||
auto out = PopupResponse::None;
|
||||
constexpr auto btnSz = ImVec2{50, BtnSz.y};
|
||||
ImGui::Separator();
|
||||
ImGui::SetCursorPosX(popupWidth - 118);
|
||||
if (ImGui::Button("OK", btnSz)) {
|
||||
if (ImGui::Button(ok.c_str(), btnSz)) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
popupOpen = false;
|
||||
out = PopupResponse::OK;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::IsKeyDown(ImGuiKey_Escape) || ImGui::Button("Cancel", btnSz)) {
|
||||
if (ImGui::IsKeyDown(ImGuiKey_Escape) || ImGui::Button(cancel.c_str(), btnSz)) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
popupOpen = false;
|
||||
out = PopupResponse::Cancel;
|
||||
@ -73,8 +77,11 @@ PopupResponse PopupControlsOkCancel(float popupWidth, bool &popupOpen) {
|
||||
return out;
|
||||
}
|
||||
|
||||
PopupResponse PopupControlsOkCancel(bool &popupOpen) {
|
||||
return PopupControlsOkCancel(ImGui::GetContentRegionAvail().x + 17, popupOpen);
|
||||
PopupResponse PopupControlsOkCancel(
|
||||
bool &popupOpen,
|
||||
ox::CStringViewCR ok,
|
||||
ox::CStringViewCR cancel) {
|
||||
return PopupControlsOkCancel(ImGui::GetContentRegionAvail().x + 17, popupOpen, ok, cancel);
|
||||
}
|
||||
|
||||
bool BeginPopup(turbine::Context &ctx, ox::CStringViewCR popupName, bool &show, ImVec2 const&sz) {
|
||||
@ -136,11 +143,12 @@ bool FileComboBox(
|
||||
bool ListBox(
|
||||
ox::CStringViewCR name,
|
||||
std::function<ox::CStringView(size_t)> const&f,
|
||||
size_t strCnt,
|
||||
size_t &selIdx) noexcept {
|
||||
size_t const strCnt,
|
||||
size_t &selIdx,
|
||||
ImVec2 const&sz) noexcept {
|
||||
auto out = false;
|
||||
if (ImGui::BeginListBox(name.c_str())) {
|
||||
for (auto i = 0u; i < strCnt; ++i) {
|
||||
if (ImGui::BeginListBox(name.c_str(), sz)) {
|
||||
for (size_t i = 0; i < strCnt; ++i) {
|
||||
auto str = f(i);
|
||||
ig::IDStackItem const idStackItem2(static_cast<int>(i));
|
||||
if (ImGui::Selectable(str.c_str(), selIdx == i)) {
|
||||
@ -161,6 +169,12 @@ bool ListBox(ox::CStringViewCR name, ox::SpanView<ox::String> const&list, size_t
|
||||
}, list.size(), selIdx);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
FilePicker::FilePicker(
|
||||
StudioContext &sctx,
|
||||
|
@ -6,11 +6,15 @@
|
||||
|
||||
namespace studio {
|
||||
|
||||
ox::Vector<EditorMaker> Module::editors(studio::StudioContext&) const {
|
||||
ox::Vector<EditorMaker> Module::editors(StudioContext&) const {
|
||||
return {};
|
||||
}
|
||||
|
||||
ox::Vector<ox::UPtr<ItemMaker>> Module::itemMakers(studio::StudioContext&) const {
|
||||
ox::Vector<ox::UPtr<ItemMaker>> Module::itemMakers(StudioContext&) const {
|
||||
return {};
|
||||
}
|
||||
|
||||
ox::Vector<ox::UPtr<ItemTemplate>> Module::itemTemplates(StudioContext&) const {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -7,11 +7,11 @@
|
||||
|
||||
namespace studio {
|
||||
|
||||
void Popup::drawWindow(turbine::Context &ctx, bool *open, std::function<void()> const&drawContents) {
|
||||
void Popup::drawWindow(turbine::Context &ctx, bool &open, std::function<void()> const&drawContents) {
|
||||
studio::ig::centerNextWindow(ctx);
|
||||
ImGui::SetNextWindowSize(static_cast<ImVec2>(m_size));
|
||||
constexpr auto modalFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
|
||||
if (ImGui::BeginPopupModal(m_title.c_str(), open, modalFlags)) {
|
||||
if (ImGui::BeginPopupModal(m_title.c_str(), &open, modalFlags)) {
|
||||
drawContents();
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
@ -59,16 +59,41 @@ ox::Error Project::mkdir(ox::StringViewCR path) const noexcept {
|
||||
auto const [stat, err] = m_fs.stat(path);
|
||||
if (err) {
|
||||
OX_RETURN_ERROR(m_fs.mkdir(path, true));
|
||||
fileUpdated.emit(path, {});
|
||||
dirAdded.emit(path);
|
||||
}
|
||||
return stat.fileType == ox::FileType::Directory ?
|
||||
ox::Error{} : ox::Error(1, "path exists as normal file");
|
||||
ox::Error{} : ox::Error{1, "path exists as normal file"};
|
||||
}
|
||||
|
||||
ox::Result<ox::FileStat> Project::stat(ox::StringViewCR path) const noexcept {
|
||||
return m_fs.stat(path);
|
||||
}
|
||||
|
||||
ox::Error Project::deleteItem(ox::StringViewCR path) noexcept {
|
||||
OX_REQUIRE(stat, m_fs.stat(path));
|
||||
if (stat.fileType == ox::FileType::Directory) {
|
||||
bool partialRemoval{};
|
||||
OX_REQUIRE(members, m_fs.ls(path));
|
||||
for (auto const&p : members) {
|
||||
partialRemoval = m_fs.remove(ox::sfmt("{}/{}", path, p)) || partialRemoval;
|
||||
}
|
||||
if (partialRemoval) {
|
||||
return ox::Error{1, "failed to remove one or more directory members"};
|
||||
}
|
||||
auto const err = m_fs.remove(path);
|
||||
if (!err) {
|
||||
fileDeleted.emit(path);
|
||||
}
|
||||
return err;
|
||||
} else {
|
||||
auto const err = m_fs.remove(path);
|
||||
if (!err) {
|
||||
fileDeleted.emit(path);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
bool Project::exists(ox::StringViewCR path) const noexcept {
|
||||
return m_fs.stat(path).error == 0;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user