Compare commits

..

19 Commits

Author SHA1 Message Date
144d234d09 [nostalgia/studio] Change version to release-d2024.12
All checks were successful
Build / build (push) Successful in 3m20s
2025-01-12 21:03:34 -06:00
465fb06f76 [nostalgia/core] Fix TileSheet validation/repair to ensure pixels gets cleared if there are subsheets
Some checks failed
Build / build (push) Has been cancelled
2025-01-12 20:58:19 -06:00
db953dd0d1 [studio] Fix new project menu to return an appropriately sized string for name 2025-01-12 20:58:19 -06:00
daab4dc4f5 [nostalgia/core/studio] Fix SubSheet editor to return an appropriately sized string 2025-01-12 20:58:19 -06:00
83cbac5cca [nostalgia/studio] Update version to d2024.12.1
All checks were successful
Build / build (push) Successful in 3m19s
2025-01-11 17:22:02 -06:00
74e518fee0 [nostalgia/core/studio/tilesheeteditor] Fix select all not to go beyond end 2025-01-11 17:07:50 -06:00
6b0ce40c02 [nostalgia/core/studio] Fix crash that occurs when a non-leaf node subsheet is selected 2025-01-11 17:07:50 -06:00
4f55964dd9 [nostalgia/core/studio] Fix tile insert to correct input when inserting past the last tile 2025-01-11 17:07:50 -06:00
cb597cf92e [nostalgia/core/studio] Fix tile insert to work on last tile 2025-01-11 17:07:50 -06:00
6906c66ba7 [keel] Fix reloadAsset 2025-01-11 17:07:50 -06:00
ba00d10dfc [nostalgia] Update liccor file 2025-01-11 17:07:50 -06:00
cdfbfe48b7 [ox] Update liccor file 2025-01-11 17:07:50 -06:00
21ae2a14e9 [ox] Run liccor 2025-01-11 17:07:50 -06:00
81b120b1ce [nostalgia,olympic] Run liccor 2025-01-11 17:07:50 -06:00
40938958de [turbine/glfw] Ensure window opens with a standard mandatory refresh period 2025-01-11 17:07:50 -06:00
df190c05a0 [studio] Make first tab not draw before selected tab when window opens 2025-01-11 17:07:50 -06:00
3fe3b08eca [ox/std] Add hash.hpp to install 2025-01-11 17:07:50 -06:00
ecc5ae6b2f [studio] Make selection tracker not go below 0 2025-01-11 17:07:50 -06:00
a863bfc181 [nostalgia/studio] Update version to d2024.12.0
All checks were successful
Build / build (push) Successful in 3m20s
2024-12-21 19:27:00 -06:00
88 changed files with 532 additions and 1503 deletions

2
.gitignore vendored
View File

@ -6,7 +6,7 @@
.mypy_cache .mypy_cache
.stfolder .stfolder
.stignore .stignore
util/scripts/__pycache__ scripts/__pycache__
pyenv pyenv
CMakeLists.txt.user CMakeLists.txt.user
ROM.oxfs ROM.oxfs

View File

@ -1,4 +1,4 @@
FROM fedora:41 FROM fedora:36
RUN dnf update -y RUN dnf update -y

View File

@ -1,29 +1,23 @@
BC_VAR_PROJECT_NAME=nostalgia BC_VAR_PROJECT_NAME=nostalgia
BC_VAR_PROJECT_NAME_CAP=Nostalgia BC_VAR_PROJECT_NAME_CAP=Nostalgia
BC_VAR_DEVENV_ROOT=util
BUILDCORE_PATH=deps/buildcore BUILDCORE_PATH=deps/buildcore
include ${BUILDCORE_PATH}/base.mk include ${BUILDCORE_PATH}/base.mk
ifeq ($(BC_VAR_OS),darwin) ifeq ($(BC_VAR_OS),darwin)
PROJECT_PLAYER=./build/${BC_VAR_CURRENT_BUILD}/bin/${BC_VAR_PROJECT_NAME}.app/Contents/MacOS/${BC_VAR_PROJECT_NAME}
NOSTALGIA_STUDIO=./build/${BC_VAR_CURRENT_BUILD}/bin/${BC_VAR_PROJECT_NAME_CAP}Studio.app/Contents/MacOS/${BC_VAR_PROJECT_NAME_CAP}Studio NOSTALGIA_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 MGBA=/Applications/mGBA.app/Contents/MacOS/mGBA
else else
PROJECT_PLAYER=./build/${BC_VAR_CURRENT_BUILD}/bin/${BC_VAR_PROJECT_NAME}
NOSTALGIA_STUDIO=./build/${BC_VAR_CURRENT_BUILD}/bin/${BC_VAR_PROJECT_NAME_CAP}Studio NOSTALGIA_STUDIO=./build/${BC_VAR_CURRENT_BUILD}/bin/${BC_VAR_PROJECT_NAME_CAP}Studio
MGBA=mgba-qt MGBA=mgba-qt
endif endif
.PHONY: pkg-gba .PHONY: pkg-gba
pkg-gba: build pkg-gba: build
${BC_CMD_ENVRUN} ${BC_PY3} ./util/scripts/pkg-gba.py sample_project ${BC_VAR_PROJECT_NAME} ${BC_CMD_ENVRUN} ${BC_PY3} ./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}
.PHONY: run .PHONY: run
run: build-player run: build
${PROJECT_PLAYER} sample_project ./build/${BC_VAR_CURRENT_BUILD}/bin/${BC_VAR_PROJECT_NAME} sample_project
.PHONY: run-studio .PHONY: run-studio
run-studio: build run-studio: build
${NOSTALGIA_STUDIO} ${NOSTALGIA_STUDIO}

View File

@ -1,8 +1,8 @@
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake/modules) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake/modules)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -nostdlib") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -nostdlib")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -nostdinc++") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -nostdinc++")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-unwind-tables") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-unwind-tables")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-strict-aliasing") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-strict-aliasing")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mthumb-interwork") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mthumb-interwork")

View File

@ -1,11 +1,2 @@
add_library(glad src/glad.c) add_library(glad OBJECT src/glad.c)
target_include_directories(glad PUBLIC include)
target_include_directories(glad PUBLIC include)
install(
TARGETS
glad
DESTINATION
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)

View File

@ -6,7 +6,7 @@ endif()
# DrinkingTea: end # DrinkingTea: end
add_library( add_library(
imgui imgui OBJECT
imgui.cpp imgui.cpp
imgui_demo.cpp imgui_demo.cpp
imgui_draw.cpp imgui_draw.cpp
@ -19,12 +19,4 @@ add_library(
target_include_directories( target_include_directories(
imgui SYSTEM PUBLIC imgui SYSTEM PUBLIC
. .
) )
install(
TARGETS
imgui
DESTINATION
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)

View File

@ -215,7 +215,7 @@ Error Signal<Args...>::emitCheckError(Args... args) const noexcept {
} }
return ox::Error(0); return ox::Error(0);
} catch (const ox::Exception &ex) { } catch (const ox::Exception &ex) {
return ox::Error(ex.errCode, ex.msg, ex.src); return ox::Error(ex.file, ex.line, ex.errCode, ex.msg);
} }
} }

View File

@ -63,6 +63,18 @@ 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 { Error FileSystem::write(const FileAddress &addr, const void *buffer, uint64_t size, FileType fileType) noexcept {
switch (addr.type()) { switch (addr.type()) {
case FileAddressType::Inode: case FileAddressType::Inode:

View File

@ -57,9 +57,9 @@ class FileSystem {
virtual Result<Vector<String>> ls(StringViewCR dir) const noexcept = 0; virtual Result<Vector<String>> ls(StringViewCR dir) const noexcept = 0;
Error remove(StringViewCR path, bool recursive = false) noexcept { virtual Error remove(StringViewCR path, bool recursive) noexcept = 0;
return removePath(path, recursive);
} Error remove(const FileAddress &addr, bool recursive = false) noexcept;
virtual Error resize(uint64_t size, void *buffer) noexcept = 0; virtual Error resize(uint64_t size, void *buffer) noexcept = 0;
@ -142,8 +142,6 @@ 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 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 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; virtual Error writeFileInode(uint64_t inode, const void *buffer, uint64_t size, FileType fileType) noexcept = 0;
@ -211,8 +209,6 @@ 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 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<const char*> directAccessInode(uint64_t) const noexcept override;
Result<Vector<String>> ls(StringViewCR dir) const noexcept override; Result<Vector<String>> ls(StringViewCR dir) const noexcept override;
@ -220,6 +216,8 @@ class FileSystemTemplate: public MemFS {
template<typename F> template<typename F>
Error ls(StringViewCR path, F cb) const; Error ls(StringViewCR path, F cb) const;
Error remove(StringViewCR path, bool recursive) noexcept override;
/** /**
* Resizes FileSystem to minimum possible size. * Resizes FileSystem to minimum possible size.
*/ */
@ -358,25 +356,6 @@ Error FileSystemTemplate<FileStore, Directory>::readFileInodeRange(uint64_t inod
return m_fs.read(inode, readStart, readSize, reinterpret_cast<uint8_t*>(buffer), size); 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> template<typename FileStore, typename Directory>
Result<const char*> FileSystemTemplate<FileStore, Directory>::directAccessInode(uint64_t inode) const noexcept { Result<const char*> FileSystemTemplate<FileStore, Directory>::directAccessInode(uint64_t inode) const noexcept {
auto data = m_fs.read(inode); auto data = m_fs.read(inode);
@ -405,6 +384,25 @@ Error FileSystemTemplate<FileStore, Directory>::ls(StringViewCR path, F cb) cons
return dir.ls(cb); 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> template<typename FileStore, typename Directory>
Error FileSystemTemplate<FileStore, Directory>::resize() noexcept { Error FileSystemTemplate<FileStore, Directory>::resize() noexcept {
return m_fs.resize(); return m_fs.resize();

View File

@ -75,6 +75,14 @@ Result<Vector<String>> PassThroughFS::ls(StringViewCR dir) const noexcept {
return out; 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 { Error PassThroughFS::resize(uint64_t, void*) noexcept {
// unsupported // unsupported
return ox::Error(1, "resize is not supported by PassThroughFS"); return ox::Error(1, "resize is not supported by PassThroughFS");
@ -159,14 +167,6 @@ 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"); 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 { Error PassThroughFS::writeFilePath(StringViewCR path, const void *buffer, uint64_t size, FileType) noexcept {
const auto p = (m_path / stripSlash(path)); const auto p = (m_path / stripSlash(path));
try { try {

View File

@ -45,6 +45,8 @@ class PassThroughFS: public FileSystem {
template<typename F> template<typename F>
Error ls(StringViewCR dir, F cb) const noexcept; 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; Error resize(uint64_t size, void *buffer) noexcept override;
Result<FileStat> statInode(uint64_t inode) const noexcept override; Result<FileStat> statInode(uint64_t inode) const noexcept override;
@ -73,8 +75,6 @@ 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 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 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; Error writeFileInode(uint64_t inode, const void *buffer, uint64_t size, FileType fileType) noexcept override;

View File

@ -14,7 +14,7 @@
{ \ { \
const auto loggerErr = (loggerName).initConn(appName); \ const auto loggerErr = (loggerName).initConn(appName); \
if (loggerErr) { \ if (loggerErr) { \
oxErrf("Could not connect to logger: {} ({}:{})\n", toStr(loggerErr), loggerErr.src.file_name(), loggerErr.src.line()); \ oxErrf("Could not connect to logger: {} ({}:{})\n", toStr(loggerErr), loggerErr.file, loggerErr.line); \
} else { \ } else { \
ox::trace::setLogger(&(loggerName)); \ ox::trace::setLogger(&(loggerName)); \
} \ } \

View File

@ -140,16 +140,16 @@ constexpr Str getModelTypeName() noexcept {
return out; return out;
} }
template<typename T, typename Str = const char*> template<typename T>
[[nodiscard]] [[nodiscard]]
consteval auto requireModelTypeName() noexcept { consteval auto requireModelTypeName() noexcept {
constexpr auto name = getModelTypeName<T, Str>(); constexpr auto name = getModelTypeName<T>();
static_assert(ox::StringView{name}.len(), "Type lacks required TypeName"); static_assert(ox::StringView{name}.len(), "Type lacks required TypeName");
return name; return name;
} }
template<typename T, typename Str = const char*> template<typename T, typename Str = const char*>
constexpr auto ModelTypeName_v = requireModelTypeName<T, Str>(); constexpr auto ModelTypeName_v = getModelTypeName<T, Str>();
template<typename T, typename Str = const char*> template<typename T, typename Str = const char*>
constexpr auto ModelTypeVersion_v = requireModelTypeVersion<T>(); constexpr auto ModelTypeVersion_v = requireModelTypeVersion<T>();

View File

@ -9,25 +9,13 @@
#pragma once #pragma once
#include "def.hpp" #include "def.hpp"
#include "error.hpp"
OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage) OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage)
namespace ox { namespace ox {
template<typename It, typename T> template<typename It, typename T>
constexpr ox::Result<size_t> findIdx(It begin, It end, T const&value) { constexpr It find(It begin, It end, const T &value) {
auto it = begin;
for (; it != end; ++it) {
if (*it == value) {
return it.offset();
}
}
return ox::Error{1, "item not found"};
}
template<typename It, typename T>
constexpr It find(It begin, It end, T const&value) {
for (; begin != end; ++begin) { for (; begin != end; ++begin) {
if (*begin == value) { if (*begin == value) {
return begin; return begin;

View File

@ -15,22 +15,18 @@
namespace ox { namespace ox {
namespace detail { class AnyPtr {
template<bool unique>
class AnyPtrT {
private: private:
struct WrapBase { struct WrapBase {
virtual constexpr ~WrapBase() = default; virtual constexpr ~WrapBase() = default;
virtual constexpr WrapBase *copyTo(ox::Span<char> s) noexcept = 0; virtual constexpr WrapBase *copyTo(ox::Span<char> s) noexcept = 0;
virtual constexpr operator bool() const noexcept = 0; virtual constexpr operator bool() const noexcept = 0;
virtual void free() noexcept = 0;
}; };
template<typename T> template<typename T>
struct Wrap final: WrapBase { struct Wrap: public WrapBase {
T *data{}; T *data{};
explicit constexpr Wrap(T *pData) noexcept: data(pData) { constexpr Wrap(T *pData) noexcept: data(pData) {
} }
constexpr WrapBase *copyTo(ox::Span<char> s) noexcept override { constexpr WrapBase *copyTo(ox::Span<char> s) noexcept override {
oxAssert(s.size() >= sizeof(Wrap), "too small buffer"); oxAssert(s.size() >= sizeof(Wrap), "too small buffer");
@ -43,20 +39,16 @@ class AnyPtrT {
constexpr operator bool() const noexcept override { constexpr operator bool() const noexcept override {
return data != nullptr; return data != nullptr;
} }
constexpr void free() noexcept override {
safeDelete(data);
data = {};
}
}; };
WrapBase *m_wrapPtr{}; WrapBase *m_wrapPtr{};
ox::Array<char, sizeof(Wrap<void*>)> m_wrapData; ox::Array<char, sizeof(Wrap<void*>)> m_wrapData;
public: public:
constexpr AnyPtrT() noexcept = default; constexpr AnyPtr() noexcept = default;
template<typename T> template<typename T>
constexpr AnyPtrT(T *ptr) noexcept { constexpr AnyPtr(T *ptr) noexcept {
if (std::is_constant_evaluated()) { if (std::is_constant_evaluated()) {
m_wrapPtr = new Wrap(ptr); m_wrapPtr = new Wrap(ptr);
} else { } else {
@ -64,39 +56,22 @@ class AnyPtrT {
} }
} }
constexpr AnyPtrT(AnyPtrT const&other) noexcept { constexpr AnyPtr(AnyPtr const&other) noexcept {
if (other) { if (other) {
m_wrapPtr = other.m_wrapPtr->copyTo(m_wrapData); m_wrapPtr = other.m_wrapPtr->copyTo(m_wrapData);
} }
} }
constexpr AnyPtrT(AnyPtrT &&other) noexcept { constexpr ~AnyPtr() 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()) { if (std::is_constant_evaluated()) {
ox::safeDelete(m_wrapPtr); ox::safeDelete(m_wrapPtr);
} }
} }
template<typename T> template<typename T>
constexpr AnyPtrT &operator=(T *ptr) noexcept { constexpr AnyPtr &operator=(T *ptr) noexcept {
if constexpr(unique) {
free();
} else if (std::is_constant_evaluated()) {
ox::safeDelete(m_wrapPtr);
}
if (std::is_constant_evaluated()) { if (std::is_constant_evaluated()) {
ox::safeDelete(m_wrapPtr);
m_wrapPtr = new Wrap(ptr); m_wrapPtr = new Wrap(ptr);
} else { } else {
m_wrapPtr = new(m_wrapData.data()) Wrap(ptr); m_wrapPtr = new(m_wrapData.data()) Wrap(ptr);
@ -104,35 +79,11 @@ class AnyPtrT {
return *this; return *this;
} }
constexpr AnyPtrT &operator=(AnyPtrT const&ptr) noexcept { constexpr AnyPtr &operator=(AnyPtr const&ptr) noexcept {
if (this != &ptr) { if (this != &ptr) {
if constexpr(unique) {
free();
} else if (std::is_constant_evaluated()) {
ox::safeDelete(m_wrapPtr);
}
if (ptr) { if (ptr) {
m_wrapPtr = ptr.m_wrapPtr->copyTo(m_wrapData);
} else {
m_wrapPtr = nullptr;
}
}
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); ox::safeDelete(m_wrapPtr);
}
if (ptr) {
m_wrapPtr = ptr.m_wrapPtr->copyTo(m_wrapData); m_wrapPtr = ptr.m_wrapPtr->copyTo(m_wrapData);
if (std::is_constant_evaluated()) {
ox::safeDelete(ptr.m_wrapPtr);
ptr.m_wrapPtr = nullptr;
}
} else { } else {
m_wrapPtr = nullptr; m_wrapPtr = nullptr;
} }
@ -144,16 +95,6 @@ class AnyPtrT {
return m_wrapPtr && *m_wrapPtr; 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> template<typename T>
[[nodiscard]] [[nodiscard]]
constexpr T *get() const noexcept { constexpr T *get() const noexcept {
@ -163,12 +104,6 @@ class AnyPtrT {
return dynamic_cast<Wrap<T>*>(m_wrapPtr)->data; return dynamic_cast<Wrap<T>*>(m_wrapPtr)->data;
#endif #endif
} }
}; };
}
using AnyPtr = detail::AnyPtrT<false>;
using UAnyPtr = detail::AnyPtrT<true>;
} }

View File

@ -7,7 +7,6 @@
*/ */
#include "fmt.hpp" #include "fmt.hpp"
#include "realstd.hpp"
#include "stacktrace.hpp" #include "stacktrace.hpp"
#include "trace.hpp" #include "trace.hpp"
@ -15,14 +14,14 @@
namespace ox { namespace ox {
void panic(StringViewCR file, int const line, StringViewCR panicMsg, Error const&err) noexcept { void panic(StringViewCR file, int line, StringViewCR panicMsg, const Error &err) noexcept {
oxErrf("\033[31;1;1mPANIC:\033[0m [{}:{}]: {}\n", file, line, panicMsg); oxErrf("\033[31;1;1mPANIC:\033[0m [{}:{}]: {}\n", file, line, panicMsg);
if (err.msg) { if (err.msg) {
oxErrf("\tError Message:\t{}\n", err.msg); oxErrf("\tError Message:\t{}\n", err.msg);
} }
oxErrf("\tError Code:\t{}\n", static_cast<ErrorCode>(err)); oxErrf("\tError Code:\t{}\n", static_cast<ErrorCode>(err));
if (err.src.file_name() != nullptr) { if (err.file != nullptr) {
oxErrf("\tError Location:\t{}:{}\n", err.src.file_name(), err.src.line()); oxErrf("\tError Location:\t{}:{}\n", err.file, err.line);
} }
#ifdef OX_USE_STDLIB #ifdef OX_USE_STDLIB
printStackTrace(2); printStackTrace(2);
@ -33,19 +32,16 @@ void panic(StringViewCR file, int const line, StringViewCR panicMsg, Error const
#endif #endif
} }
void panic(const char *file, int const line, char const*panicMsg, Error const&err) noexcept { void panic(const char *file, int line, const char *panicMsg, const Error &err) noexcept {
panic(StringView{file}, line, StringView{panicMsg}, err); panic(StringView{file}, line, StringView{panicMsg}, err);
} }
void assertFailFuncRuntime( void assertFailFuncRuntime(StringViewCR file, int line, StringViewCR assertTxt, StringViewCR msg) noexcept {
StringViewCR file,
int const line,
StringViewCR assertTxt,
StringViewCR msg) noexcept {
#ifdef OX_USE_STDLIB #ifdef OX_USE_STDLIB
auto const st = genStackTrace(2); auto output = sfmt("\n\033[31;1;1mASSERT FAILURE:\033[0m [{}:{}]: {}\n", file, line, msg);
oxTracef("assert", "Failed assert: {} ({}) [{}:{}]:\n{}", msg, assertTxt, file, line, st); output += genStackTrace(2);
abort(); oxTracef("assert", "Failed assert: {} ({}) [{}:{}]", msg, assertTxt, file, line);
std::abort();
#else #else
oxErrf("\n\033[31;1;1mASSERT FAILURE:\033[0m [{}:{}]: {}\n", file, line, msg); oxErrf("\n\033[31;1;1mASSERT FAILURE:\033[0m [{}:{}]: {}\n", file, line, msg);
oxTracef("assert", "Failed assert: {} ({}) [{}:{}]", msg, assertTxt, file, line); oxTracef("assert", "Failed assert: {} ({}) [{}:{}]", msg, assertTxt, file, line);
@ -53,25 +49,20 @@ void assertFailFuncRuntime(
#endif #endif
} }
void assertFailFuncRuntime( void assertFailFuncRuntime(StringViewCR file, int line, [[maybe_unused]] const Error &err, StringViewCR, StringViewCR assertMsg) noexcept {
StringViewCR file,
int const line,
[[maybe_unused]] Error const&err,
StringViewCR,
StringViewCR assertMsg) noexcept {
#if defined(OX_USE_STDLIB) #if defined(OX_USE_STDLIB)
auto msg = sfmt("\n\033[31;1;1mASSERT FAILURE:\033[0m [{}:{}]: {}\n", file, line, assertMsg); auto msg = sfmt("\n\033[31;1;1mASSERT FAILURE:\033[0m [{}:{}]: {}\n", file, line, assertMsg);
if (err.msg) { if (err.msg) {
msg += sfmt("\tError Message:\t{}\n", err.msg); msg += sfmt("\tError Message:\t{}\n", err.msg);
} }
msg += sfmt("\tError Code:\t{}\n", static_cast<ErrorCode>(err)); msg += sfmt("\tError Code:\t{}\n", static_cast<ErrorCode>(err));
if (err.src.file_name() != nullptr) { if (err.file != nullptr) {
msg += sfmt("\tError Location:\t{}:{}\n", err.src.file_name(), err.src.line()); msg += sfmt("\tError Location:\t{}:{}\n", err.file, err.line);
} }
msg += genStackTrace(2); msg += genStackTrace(2);
oxErr(msg); oxErr(msg);
oxTracef("assert", "Failed assert: {} [{}:{}]", assertMsg, file, line); oxTracef("assert", "Failed assert: {} [{}:{}]", assertMsg, file, line);
abort(); std::abort();
#else #else
constexprPanic(file, line, assertMsg); constexprPanic(file, line, assertMsg);
#endif #endif

View File

@ -22,15 +22,9 @@
namespace ox { namespace ox {
[[noreturn]] void panic(StringViewCR file, int line, StringViewCR panicMsg, const Error &err = ox::Error(0)) noexcept;
void panic(StringViewCR file, int line, StringViewCR panicMsg, Error const&err = {}) noexcept;
[[noreturn]] constexpr void constexprPanic(StringViewCR file, int line, StringViewCR panicMsg, const Error &err = ox::Error(0)) noexcept {
constexpr void constexprPanic(
StringViewCR file,
int const line,
StringViewCR panicMsg,
Error const&err = {}) noexcept {
if (!std::is_constant_evaluated()) { if (!std::is_constant_evaluated()) {
panic(file, line, panicMsg, err); panic(file, line, panicMsg, err);
} else { } else {
@ -38,24 +32,10 @@ constexpr void constexprPanic(
} }
} }
void assertFailFuncRuntime( void assertFailFuncRuntime(StringViewCR file, int line, StringViewCR assertTxt, StringViewCR msg) noexcept;
StringViewCR file, void assertFailFuncRuntime(StringViewCR file, int line, const Error &err, StringViewCR, StringViewCR assertMsg) noexcept;
int line,
StringViewCR assertTxt,
StringViewCR msg) noexcept;
void assertFailFuncRuntime(
StringViewCR file,
int line,
Error const&err,
StringViewCR,
StringViewCR assertMsg) noexcept;
constexpr void assertFunc( constexpr void assertFunc(StringViewCR file, int line, bool pass, [[maybe_unused]]StringViewCR assertTxt, [[maybe_unused]]StringViewCR msg) noexcept {
StringViewCR file,
int const line,
bool const pass,
[[maybe_unused]]StringViewCR assertTxt,
[[maybe_unused]]StringViewCR msg) noexcept {
if (!pass) { if (!pass) {
if (!std::is_constant_evaluated()) { if (!std::is_constant_evaluated()) {
assertFailFuncRuntime(file, line, assertTxt, msg); assertFailFuncRuntime(file, line, assertTxt, msg);
@ -65,12 +45,7 @@ constexpr void assertFunc(
} }
} }
constexpr void assertFunc( constexpr void assertFunc(StringViewCR file, int line, const Error &err, StringViewCR, StringViewCR assertMsg) noexcept {
StringViewCR file,
int const line,
Error const&err,
StringViewCR,
StringViewCR assertMsg) noexcept {
if (err) { if (err) {
if (!std::is_constant_evaluated()) { if (!std::is_constant_evaluated()) {
assertFailFuncRuntime(file, line, err, {}, assertMsg); assertFailFuncRuntime(file, line, err, {}, assertMsg);
@ -80,11 +55,7 @@ constexpr void assertFunc(
} }
} }
constexpr void expect( constexpr void expect(StringViewCR file, int line, const auto &actual, const auto &expected) noexcept {
StringViewCR file,
int const line,
auto const&actual,
auto const&expected) noexcept {
if (actual != expected) { if (actual != expected) {
if (!std::is_constant_evaluated()) { if (!std::is_constant_evaluated()) {
#if defined(OX_USE_STDLIB) #if defined(OX_USE_STDLIB)

View File

@ -36,16 +36,28 @@ using ErrorCode = uint16_t;
struct [[nodiscard]] Error { struct [[nodiscard]] Error {
std::source_location src;
ox::CString msg = nullptr; ox::CString msg = nullptr;
ox::CString file = nullptr;
uint16_t line = 0;
ErrorCode errCode = 0; ErrorCode errCode = 0;
constexpr Error() noexcept = default; constexpr Error() noexcept = default;
explicit constexpr Error(
ox::CString file,
uint32_t const line,
ErrorCode const errCode,
ox::CString msg = nullptr) noexcept:
msg{msg},
file{file},
line{static_cast<uint16_t>(line)},
errCode{errCode} {}
explicit constexpr Error( explicit constexpr Error(
ErrorCode const errCode, ErrorCode const errCode,
std::source_location const&src = std::source_location::current()) noexcept: std::source_location const&src = std::source_location::current()) noexcept:
src{src}, file{src.file_name()},
line{static_cast<uint16_t>(src.line())},
errCode{errCode} errCode{errCode}
{} {}
@ -53,8 +65,9 @@ struct [[nodiscard]] Error {
ErrorCode const errCode, ErrorCode const errCode,
ox::CString msg, ox::CString msg,
std::source_location const&src = std::source_location::current()) noexcept: std::source_location const&src = std::source_location::current()) noexcept:
src{src},
msg{msg}, msg{msg},
file{src.file_name()},
line{static_cast<uint16_t>(src.line())},
errCode{errCode} errCode{errCode}
{} {}
@ -76,31 +89,42 @@ constexpr auto toStr(Error const&err) noexcept {
} }
struct Exception: public std::exception { struct Exception: public std::exception {
std::source_location src;
ox::CString msg = nullptr; ox::CString msg = nullptr;
ox::CString file = nullptr;
uint16_t line = 0;
ErrorCode errCode = 0; ErrorCode errCode = 0;
explicit Exception( explicit inline Exception(ox::CString file, uint32_t line, ErrorCode errCode, char const*msg = "") noexcept {
this->file = file;
this->line = static_cast<uint16_t>(line);
this->msg = msg;
this->errCode = errCode;
}
explicit inline Exception(
ErrorCode const errCode, ErrorCode const errCode,
std::source_location const&src = std::source_location::current()) noexcept: std::source_location const&src = std::source_location::current()) noexcept:
src{src}, file{src.file_name()},
line{static_cast<uint16_t>(src.line())},
errCode{errCode} {} errCode{errCode} {}
explicit Exception( explicit inline Exception(
ErrorCode const errCode, ErrorCode const errCode,
ox::CString msg, ox::CString msg,
std::source_location const&src = std::source_location::current()) noexcept: std::source_location const&src = std::source_location::current()) noexcept:
src{src},
msg{msg}, msg{msg},
file{src.file_name()},
line{static_cast<uint16_t>(src.line())},
errCode{errCode} {} errCode{errCode} {}
explicit Exception(Error const&err) noexcept: explicit inline Exception(Error const&err) noexcept:
src{err.src},
msg{err.msg ? err.msg : ""}, msg{err.msg ? err.msg : ""},
file{err.file},
line{err.line},
errCode{err.errCode} {} errCode{err.errCode} {}
constexpr Error toError() const noexcept { constexpr Error toError() const noexcept {
return Error(errCode, msg, src); return Error(file, line, errCode, msg);
} }
[[nodiscard]] [[nodiscard]]
@ -109,7 +133,6 @@ struct Exception: public std::exception {
} }
}; };
[[noreturn]]
void panic(char const*file, int line, char const*panicMsg, Error const&err) noexcept; void panic(char const*file, int line, char const*panicMsg, Error const&err) noexcept;
template<typename T> template<typename T>

View File

@ -31,6 +31,7 @@ concept Reader_c = requires(T v) {
class Reader_v { class Reader_v {
public: public:
virtual constexpr ~Reader_v() noexcept = default; virtual constexpr ~Reader_v() noexcept = default;
[[nodiscard]]
virtual constexpr ox::Result<char> peek() const noexcept = 0; virtual constexpr ox::Result<char> peek() const noexcept = 0;
virtual constexpr ox::Result<std::size_t> read(char*, std::size_t) noexcept = 0; virtual constexpr ox::Result<std::size_t> read(char*, std::size_t) noexcept = 0;
virtual constexpr ox::Result<std::size_t> tellg() noexcept = 0; virtual constexpr ox::Result<std::size_t> tellg() noexcept = 0;

View File

@ -12,13 +12,4 @@
#include <cassert> #include <cassert>
#else #else
#define assert(e) while (!(e)); #define assert(e) while (!(e));
#endif
#if __has_include(<cstdlib>)
#include <cstdlib>
#else
extern "C" {
[[noreturn]]
void abort();
}
#endif #endif

View File

@ -62,9 +62,6 @@ class SmallMap {
[[nodiscard]] [[nodiscard]]
constexpr Vector<K> keys() const noexcept; constexpr Vector<K> keys() const noexcept;
[[nodiscard]]
constexpr Vector<T> values() const noexcept;
[[nodiscard]] [[nodiscard]]
constexpr K const&key(size_t i) const noexcept; constexpr K const&key(size_t i) const noexcept;
@ -85,22 +82,14 @@ class SmallMap {
return m_pairs; return m_pairs;
} }
[[nodiscard]]
constexpr ox::Span<Pair> pairs() noexcept {
return m_pairs;
}
constexpr void clear(); constexpr void clear();
private: private:
template<typename KK> template<typename KK>
constexpr Pair const*access(PairVector const&pairs, KK const&key, bool &isNew) const; constexpr Pair const&access(PairVector const&pairs, KK const&key, bool &isNew) const;
template<typename KK> template<typename KK>
constexpr Pair *access(PairVector &pairs, KK const&key, bool &isNew); constexpr Pair &access(PairVector &pairs, KK const&key, bool &isNew);
template<typename KK>
constexpr Pair *accessNoCreate(PairVector &pairs, KK const&key);
}; };
@ -140,7 +129,7 @@ constexpr SmallMap<K, T, SmallSz> &SmallMap<K, T, SmallSz>::operator=(SmallMap<K
template<typename K, typename T, size_t SmallSz> template<typename K, typename T, size_t SmallSz>
constexpr T &SmallMap<K, T, SmallSz>::operator[](MaybeView_t<K> const&k) { constexpr T &SmallMap<K, T, SmallSz>::operator[](MaybeView_t<K> const&k) {
bool isNew{}; bool isNew{};
auto p = access(m_pairs, k, isNew); auto p = &access(m_pairs, k, isNew);
if (isNew) { if (isNew) {
p->key = k; p->key = k;
} }
@ -149,7 +138,7 @@ constexpr T &SmallMap<K, T, SmallSz>::operator[](MaybeView_t<K> const&k) {
template<typename K, typename T, size_t SmallSz> template<typename K, typename T, size_t SmallSz>
constexpr Result<T*> SmallMap<K, T, SmallSz>::at(MaybeView_t<K> const&k) noexcept { constexpr Result<T*> SmallMap<K, T, SmallSz>::at(MaybeView_t<K> const&k) noexcept {
auto const p = accessNoCreate(m_pairs, k); auto p = access(m_pairs, k);
if (!p) { if (!p) {
return {nullptr, ox::Error(1, "value not found for given key")}; return {nullptr, ox::Error(1, "value not found for given key")};
} }
@ -158,8 +147,7 @@ constexpr Result<T*> SmallMap<K, T, SmallSz>::at(MaybeView_t<K> const&k) noexcep
template<typename K, typename T, size_t SmallSz> template<typename K, typename T, size_t SmallSz>
constexpr Result<const T*> SmallMap<K, T, SmallSz>::at(MaybeView_t<K> const&k) const noexcept { constexpr Result<const T*> SmallMap<K, T, SmallSz>::at(MaybeView_t<K> const&k) const noexcept {
bool isNew{}; auto p = access(m_pairs, k);
auto p = access(m_pairs, k, isNew);
if (!p) { if (!p) {
return {nullptr, ox::Error(1, "value not found for given key")}; return {nullptr, ox::Error(1, "value not found for given key")};
} }
@ -180,8 +168,7 @@ constexpr void SmallMap<K, T, SmallSz>::erase(MaybeView_t<K> const&k) {
template<typename K, typename T, size_t SmallSz> template<typename K, typename T, size_t SmallSz>
constexpr bool SmallMap<K, T, SmallSz>::contains(MaybeView_t<K> const&k) const noexcept { constexpr bool SmallMap<K, T, SmallSz>::contains(MaybeView_t<K> const&k) const noexcept {
bool isNew{}; return access(m_pairs, k) != nullptr;
return access(m_pairs, k, isNew) != nullptr;
} }
template<typename K, typename T, size_t SmallSz> template<typename K, typename T, size_t SmallSz>
@ -199,16 +186,6 @@ constexpr Vector<K> SmallMap<K, T, SmallSz>::keys() const noexcept {
return keys; return keys;
} }
template<typename K, typename T, size_t SmallSz>
constexpr Vector<T> SmallMap<K, T, SmallSz>::values() const noexcept {
ox::Vector<T> keys;
keys.reserve(m_pairs.size());
for (auto const&p : m_pairs) {
keys.emplace_back(p.key);
}
return keys;
}
template<typename K, typename T, size_t SmallSz> template<typename K, typename T, size_t SmallSz>
constexpr K const&SmallMap<K, T, SmallSz>::key(size_t i) const noexcept { constexpr K const&SmallMap<K, T, SmallSz>::key(size_t i) const noexcept {
return m_pairs[i].key; return m_pairs[i].key;
@ -241,42 +218,30 @@ constexpr void SmallMap<K, T, SmallSz>::clear() {
template<typename K, typename T, size_t SmallSz> template<typename K, typename T, size_t SmallSz>
template<typename KK> template<typename KK>
constexpr typename SmallMap<K, T, SmallSz>::Pair const*SmallMap<K, T, SmallSz>::access( constexpr typename SmallMap<K, T, SmallSz>::Pair const&SmallMap<K, T, SmallSz>::access(
PairVector const&pairs, KK const&k, bool &isNew) const { PairVector const&pairs, KK const&k, bool &isNew) const {
for (auto const&p : pairs) { for (auto const&p : pairs) {
if (p.key == k) { if (p.key == k) {
isNew = false; isNew = false;
return &p; return p;
} }
} }
isNew = true; isNew = true;
return nullptr; return pairs.emplace_back();
} }
template<typename K, typename T, size_t SmallSz> template<typename K, typename T, size_t SmallSz>
template<typename KK> template<typename KK>
constexpr typename SmallMap<K, T, SmallSz>::Pair *SmallMap<K, T, SmallSz>::access( constexpr typename SmallMap<K, T, SmallSz>::Pair &SmallMap<K, T, SmallSz>::access(
PairVector &pairs, KK const&k, bool &isNew) { PairVector &pairs, KK const&k, bool &isNew) {
for (auto &p : pairs) { for (auto &p : pairs) {
if (p.key == k) { if (p.key == k) {
isNew = false; isNew = false;
return &p; return p;
} }
} }
isNew = true; isNew = true;
return &pairs.emplace_back(); return pairs.emplace_back();
}
template<typename K, typename T, size_t SmallSz>
template<typename KK>
constexpr typename SmallMap<K, T, SmallSz>::Pair *SmallMap<K, T, SmallSz>::accessNoCreate(
PairVector &pairs, KK const&k) {
for (auto &p : pairs) {
if (p.key == k) {
return &p;
}
}
return nullptr;
} }
template<typename T, typename K, typename V, size_t SmallSz> template<typename T, typename K, typename V, size_t SmallSz>

View File

@ -14,7 +14,7 @@
#include "iterator.hpp" #include "iterator.hpp"
#include "vector.hpp" #include "vector.hpp"
OX_ALLOW_UNSAFE_BUFFERS_BEGIN OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage)
namespace ox { namespace ox {
@ -133,7 +133,7 @@ class Span {
return m_items[i]; return m_items[i];
} }
constexpr T const&operator[](std::size_t i) const noexcept { constexpr const T &operator[](std::size_t i) const noexcept {
ox::primitiveAssert(__FILE__, __LINE__, i < size(), "Span access overflow"); ox::primitiveAssert(__FILE__, __LINE__, i < size(), "Span access overflow");
return m_items[i]; return m_items[i];
} }
@ -168,20 +168,8 @@ class Span {
}; };
template<typename T> template<typename T>
using SpanView = Span<T const>; using SpanView = Span<const T>;
template<typename T>
constexpr void spancpy(ox::Span<T> const dst, ox::SpanView<T> const src) noexcept {
auto const sz = ox::min(dst.size(), src.size());
if (std::is_constant_evaluated() || std::is_trivially_copyable_v<T>) {
for (size_t i{}; i < sz; ++i) {
dst.data()[i] = src.data()[i];
}
} else {
memcpy(dst.data(), src.data(), sz * sizeof(T));
}
}
} }
OX_ALLOW_UNSAFE_BUFFERS_END OX_CLANG_NOWARN_END

View File

@ -17,8 +17,6 @@ add_test("[ox/std] String" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest "String")
add_test("[ox/std] SmallMap" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest "SmallMap") add_test("[ox/std] SmallMap" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest "SmallMap")
add_test("[ox/std] SmallMap2" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest "SmallMap2") add_test("[ox/std] SmallMap2" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest "SmallMap2")
add_test("[ox/std] Vector" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest "Vector") add_test("[ox/std] Vector" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest "Vector")
add_test("[ox/std] Vector::shrink_to_fit" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest "Vector::shrink_to_fit")
add_test("[ox/std] findIdx" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest "findIdx")
add_test("[ox/std] HashMap" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest "HashMap") add_test("[ox/std] HashMap" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest "HashMap")
add_test("[ox/std] HeapMgr" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest malloc) add_test("[ox/std] HeapMgr" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest malloc)
add_test("[ox/std] Serialize-Int" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest "Serialize-Int") add_test("[ox/std] Serialize-Int" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest "Serialize-Int")

View File

@ -237,50 +237,6 @@ OX_CLANG_NOWARN_END
return ox::Error(0); return ox::Error(0);
} }
}, },
{
"Vector::shrink_to_fit",
[] {
{
ox::Vector<ox::IString<8>> v;
v.reserve(50);
v.emplace_back("asdf");
v.emplace_back("aoeu");
auto const origData = v.data();
v.shrink_to_fit();
oxExpect(v[0], "asdf");
oxExpect(v[1], "aoeu");
oxExpect(v.capacity(), 2u);
oxAssert(origData != v.data(), "shrink_to_fit did not create a new allocation");
}
{
ox::Vector<ox::IString<8>> v;
v.reserve(2);
v.emplace_back("asdf");
v.emplace_back("aoeu");
auto const origData = v.data();
v.shrink_to_fit();
oxExpect(v[0], "asdf");
oxExpect(v[1], "aoeu");
oxExpect(v.capacity(), 2u);
oxAssert(origData == v.data(), "shrink_to_fit inappropriately created a new allocation");
}
return ox::Error{};
}
},
{
"findIdx",
[] {
ox::Vector<ox::IString<8>> const v {"zero", "one", "two", "three", "four"};
oxExpect(ox::findIdx(v.begin(), v.end(), "zero").or_value(5), 0u);
oxExpect(ox::findIdx(v.begin(), v.end(), "one").or_value(5), 1u);
oxExpect(ox::findIdx(v.begin(), v.end(), "two").or_value(5), 2u);
oxExpect(ox::findIdx(v.begin(), v.end(), "three").or_value(5), 3u);
oxExpect(ox::findIdx(v.begin(), v.end(), "four").or_value(5), 4u);
oxExpect(ox::findIdx(v.begin(), v.end(), "five").or_value(5), 5u);
oxExpect(ox::findIdx(v.begin(), v.end(), "six").or_value(6), 6u);
return ox::Error{};
}
},
{ {
"SmallMap", "SmallMap",
[] { [] {
@ -290,18 +246,7 @@ OX_CLANG_NOWARN_END
oxExpect(map.size(), 1u); oxExpect(map.size(), 1u);
oxExpect(map["aoeu"], ""); oxExpect(map["aoeu"], "");
oxExpect(map.size(), 2u); oxExpect(map.size(), 2u);
ox::SmallMap<ox::String, ox::String> cmap; return ox::Error(0);
cmap["asdf"] = "aoeu";
auto constexpr constTest = [](ox::SmallMap<ox::String, ox::String> const&map) {
OX_REQUIRE(asdf, map.at("asdf"));
oxExpect(*asdf, "aoeu");
oxExpect(map.size(), 1u);
auto const aoeu = map.at("aoeu");
oxExpect(aoeu.ok(), false);
oxExpect(map.size(), 1u);
return ox::Error{};
};
return constTest(cmap);
} }
}, },
{ {

View File

@ -269,8 +269,8 @@ using TraceStream = NullStream;
inline void logError(const char *file, int line, const char *fmt, const Error &err) noexcept { inline void logError(const char *file, int line, const char *fmt, const Error &err) noexcept {
if (err) { if (err) {
TraceStream trc(file, line, "ox::error"); TraceStream trc(file, line, "ox::error");
if (err.src.file_name() != nullptr) { if (err.file != nullptr) {
trc << "Error: (" << err.src.file_name() << ":" << err.src.line() << "):"; trc << "Error: (" << err.file << ":" << err.line << "):";
} else { } else {
trc << "Error:"; trc << "Error:";
} }
@ -282,8 +282,8 @@ inline void logError(const char *file, int line, const Error &err) noexcept {
if (err) { if (err) {
TraceStream trc(file, line, "ox::error"); TraceStream trc(file, line, "ox::error");
trc << "Error:" << err; trc << "Error:" << err;
if (err.src.file_name() != nullptr) { if (err.file != nullptr) {
trc << "(" << err.src.file_name() << ":" << err.src.line() << ")"; trc << "(" << err.file << ":" << err.line << ")";
} }
} }
} }

View File

@ -19,15 +19,12 @@
namespace std { namespace std {
template<typename T> template<typename T>
inline constexpr bool is_union_v = __is_union(T); constexpr bool is_union_v = __is_union(T);
inline constexpr bool is_constant_evaluated() noexcept { constexpr bool is_constant_evaluated() noexcept {
return __builtin_is_constant_evaluated(); return __builtin_is_constant_evaluated();
} }
template<typename T>
inline constexpr bool is_trivially_copyable_v = __is_trivially_copyable(T);
} }
#endif #endif
@ -159,9 +156,6 @@ static_assert(is_class<int>::value == false);
template<typename T> template<typename T>
constexpr bool is_class_v = is_class<T>(); constexpr bool is_class_v = is_class<T>();
template<typename T>
inline constexpr bool is_trivially_copyable_v = std::is_trivially_copyable_v<T>;
template<typename T> template<typename T>
constexpr bool is_signed_v = integral_constant<bool, T(-1) < T(0)>::value; constexpr bool is_signed_v = integral_constant<bool, T(-1) < T(0)>::value;

View File

@ -313,8 +313,6 @@ class Vector: detail::VectorAllocator<T, Allocator, SmallVectorSize> {
constexpr void reserve(std::size_t cap) noexcept(useNoexcept); constexpr void reserve(std::size_t cap) noexcept(useNoexcept);
constexpr void shrink_to_fit() noexcept(useNoexcept);
private: private:
constexpr void reserveInsert( constexpr void reserveInsert(
std::size_t cap, std::size_t pos, std::size_t offset = 1) noexcept(useNoexcept); std::size_t cap, std::size_t pos, std::size_t offset = 1) noexcept(useNoexcept);
@ -343,7 +341,6 @@ constexpr Vector<T, SmallVectorSize, Allocator>::Vector(std::size_t size) noexce
template<typename T, std::size_t SmallVectorSize, typename Allocator> template<typename T, std::size_t SmallVectorSize, typename Allocator>
constexpr Vector<T, SmallVectorSize, Allocator>::Vector(std::initializer_list<T> list) noexcept { constexpr Vector<T, SmallVectorSize, Allocator>::Vector(std::initializer_list<T> list) noexcept {
reserve(list.size());
for (auto &item : list) { for (auto &item : list) {
emplace_back(std::move(item)); emplace_back(std::move(item));
} }
@ -678,24 +675,6 @@ constexpr void Vector<T, SmallVectorSize, Allocator>::reserve(std::size_t cap) n
} }
} }
template<typename T, std::size_t SmallVectorSize, typename Allocator>
constexpr void Vector<T, SmallVectorSize, Allocator>::shrink_to_fit() noexcept(useNoexcept) {
if (m_size == m_cap) {
return;
}
const auto oldItems = m_items;
const auto oldCap = m_cap;
m_cap = m_size;
this->allocate(&m_items, m_size);
if (oldItems) { // move over old items
for (std::size_t i = 0; i < m_size; ++i) {
std::construct_at(&m_items[i], std::move(oldItems[i]));
oldItems[i].~T();
}
this->deallocate(oldItems, oldCap);
}
}
template<typename T, std::size_t SmallVectorSize, typename Allocator> template<typename T, std::size_t SmallVectorSize, typename Allocator>
constexpr void Vector<T, SmallVectorSize, Allocator>::reserveInsert( constexpr void Vector<T, SmallVectorSize, Allocator>::reserveInsert(
std::size_t cap, std::size_t cap,

View File

@ -4,20 +4,17 @@
#include <ox/std/heapmgr.hpp> #include <ox/std/heapmgr.hpp>
#include <teagba/bios.hpp>
#include <teagba/registers.hpp>
namespace mgba { namespace mgba {
void initConsole(); void initConsole();
} }
#define MEM_HEAP_BEGIN reinterpret_cast<char*>(0x02000000) #define MEM_EWRAM_BEGIN reinterpret_cast<char*>(0x02000000)
#define MEM_HEAP_END reinterpret_cast<char*>(0x0203FFFF) #define MEM_EWRAM_END reinterpret_cast<char*>(0x0203FFFF)
#define HEAP_BEGIN reinterpret_cast<char*>(MEM_HEAP_BEGIN) #define HEAP_BEGIN reinterpret_cast<char*>(MEM_EWRAM_BEGIN)
// set size to half of EWRAM // set size to half of EWRAM
#define HEAP_SIZE ((MEM_HEAP_END - MEM_HEAP_BEGIN) / 2) #define HEAP_SIZE ((MEM_EWRAM_END - MEM_EWRAM_BEGIN) / 2)
#define HEAP_END reinterpret_cast<char*>(MEM_HEAP_BEGIN + HEAP_SIZE) #define HEAP_END reinterpret_cast<char*>(MEM_EWRAM_BEGIN + HEAP_SIZE)
extern void (*__preinit_array_start[]) (void); extern void (*__preinit_array_start[]) (void);
extern void (*__preinit_array_end[]) (void); extern void (*__preinit_array_end[]) (void);
@ -28,14 +25,6 @@ int main(int argc, const char **argv);
extern "C" { extern "C" {
void abort() {
REG_IE = 0;
teagba::intrwait(0, 0);
while (true);
}
void *__gxx_personality_v0{};
void __libc_init_array() { void __libc_init_array() {
auto preInits = __preinit_array_end - __preinit_array_start; auto preInits = __preinit_array_end - __preinit_array_start;
for (decltype(preInits) i = 0; i < preInits; i++) { for (decltype(preInits) i = 0; i < preInits; i++) {

View File

@ -162,9 +162,11 @@ The Ox way of doing things is the Olympic way of doing things.
### Error Handling ### Error Handling
Instead of throwing exceptions, generally try to use The GBA build has exceptions disabled.
[ox::Errors](deps/ox/ox-docs.md#error-handling) for error reporting, Instead of throwing exceptions, all engine code should return
but exceptions may be used where they make sense. [ox::Errors](deps/ox/ox-docs.md#error-handling) for error reporting.
For the sake of consistency, try to stick to ```ox::Error``` in non-engine code
as well, but non-engine code is free to use exceptions when they make sense.
Exceptions should generally just use ```OxException```, which is bascially an Exceptions should generally just use ```OxException```, which is bascially an
exception form of ```ox::Error```. exception form of ```ox::Error```.

View File

@ -28,11 +28,11 @@ project_name = sys.argv[2]
bin = f'./build/{host_env}-{current_build}/bin/' bin = f'./build/{host_env}-{current_build}/bin/'
project_bin = f'build/gba-release/bin/{project_name}.bin' project_bin = f'build/gba-release/bin/{project_name}.bin'
project_gba = f'{project_name}.gba' project_gba = f'{project_name}.gba'
project_manifest = f'{project_name.lower()}-manifest.json' project_manifest = f'{project_name}-manifest.json'
shutil.copyfile(project_bin, project_gba) shutil.copyfile(project_bin, project_gba)
subprocess.run([ subprocess.run([
f'{bin}/{project_name.lower()}-pack', f'{bin}/{project_name}-pack',
'-src', project_dir, '-src', project_dir,
'-rom-bin', project_gba, '-rom-bin', project_gba,
'-manifest', project_manifest]) '-manifest', project_manifest])

View File

@ -17,9 +17,11 @@ namespace nostalgia::core {
class Context; class Context;
void safeDelete(Context *ctx) noexcept; struct ContextDeleter {
void operator()(Context *p) noexcept;
};
using ContextUPtr = ox::UPtr<Context>; using ContextUPtr = ox::UPtr<Context, ContextDeleter>;
ox::Result<ContextUPtr> init(turbine::Context &tctx, InitParams const&params = {}) noexcept; ox::Result<ContextUPtr> init(turbine::Context &tctx, InitParams const&params = {}) noexcept;

View File

@ -102,6 +102,8 @@ OX_MODEL_BEGIN(TileSheetSet)
OX_MODEL_FIELD(entries) OX_MODEL_FIELD(entries)
OX_MODEL_END() OX_MODEL_END()
void addEntry(TileSheetSet &set, ox::FileAddress path, int32_t begin = 0, int32_t size = -1) noexcept;
[[nodiscard]] [[nodiscard]]
int tileColumns(Context&) noexcept; int tileColumns(Context&) noexcept;
@ -142,10 +144,6 @@ ox::Error loadBgTileSheet(
unsigned cbb, unsigned cbb,
TileSheetSet const&set) noexcept; TileSheetSet const&set) noexcept;
void clearCbb(Context &ctx, unsigned cbb) noexcept;
void clearCbbs(Context &ctx) noexcept;
ox::Error loadBgTileSheet( ox::Error loadBgTileSheet(
Context &ctx, Context &ctx,
unsigned cbb, unsigned cbb,

View File

@ -1,11 +0,0 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#pragma once
#include <studio/studio.hpp>
namespace nostalgia::core {
}

View File

@ -18,23 +18,6 @@
namespace nostalgia::core { namespace nostalgia::core {
struct SubSheetTemplate {
static constexpr auto TypeName = "net.drinkingtea.nostalgia.gfx.SubSheetTemplate";
static constexpr auto TypeVersion = 1;
ox::String name;
int32_t width{};
int32_t height{};
ox::Vector<SubSheetTemplate> subsheets;
};
OX_MODEL_BEGIN(SubSheetTemplate)
OX_MODEL_FIELD(name)
OX_MODEL_FIELD(width)
OX_MODEL_FIELD(height)
OX_MODEL_FIELD(subsheets)
OX_MODEL_END()
// Predecessor to TileSheet, kept for backward compatibility // Predecessor to TileSheet, kept for backward compatibility
struct TileSheetV1 { struct TileSheetV1 {
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.NostalgiaGraphic"; static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.NostalgiaGraphic";
@ -229,10 +212,6 @@ struct TileSheetV4 {
pixels(std::move(pPixels)) { pixels(std::move(pPixels)) {
} }
/**
*
* @return the dimensional size of the SubSheet (e.g. width * height)
*/
[[nodiscard]] [[nodiscard]]
constexpr std::size_t size() const noexcept { constexpr std::size_t size() const noexcept {
return static_cast<std::size_t>(columns) * static_cast<std::size_t>(rows); return static_cast<std::size_t>(columns) * static_cast<std::size_t>(rows);
@ -302,7 +281,7 @@ size_t getTileCnt(TileSheet const&ts) noexcept;
TileSheet::SubSheet const*getSubsheet(TileSheet const&ts, SubSheetId id) noexcept; TileSheet::SubSheet const*getSubsheet(TileSheet const&ts, SubSheetId id) noexcept;
[[nodiscard]] [[nodiscard]]
ox::Optional<size_t> getTileIdx(TileSheet const&ts, SubSheetId id) noexcept; size_t getTileIdx(TileSheet const&ts, SubSheetId id) noexcept;
[[nodiscard]] [[nodiscard]]
uint8_t getPixel4Bpp(TileSheet::SubSheet const&ss, std::size_t idx) noexcept; uint8_t getPixel4Bpp(TileSheet::SubSheet const&ss, std::size_t idx) noexcept;
@ -421,8 +400,6 @@ ox::Result<SubSheetId> getIdFor(TileSheet const&ts, ox::StringViewCR path) noexc
ox::Result<unsigned> getTileOffset(TileSheet const&ts, ox::StringViewCR pNamePath) noexcept; ox::Result<unsigned> getTileOffset(TileSheet const&ts, ox::StringViewCR pNamePath) noexcept;
ox::Result<uint32_t> getTileOffset(TileSheet const&ts, SubSheetId pId) noexcept;
ox::Result<ox::StringView> getNameFor(TileSheet::SubSheet const&ss, SubSheetId pId) noexcept; ox::Result<ox::StringView> getNameFor(TileSheet::SubSheet const&ss, SubSheetId pId) noexcept;
ox::Result<ox::StringView> getNameFor(TileSheet const&ts, SubSheetId pId) noexcept; ox::Result<ox::StringView> getNameFor(TileSheet const&ts, SubSheetId pId) noexcept;

View File

@ -10,8 +10,8 @@
namespace nostalgia::core { namespace nostalgia::core {
void safeDelete(Context *ctx) noexcept { void ContextDeleter::operator()(Context *p) noexcept {
delete ctx; ox::safeDelete(p);
} }
Context::Context(turbine::Context &tctx) noexcept: turbineCtx(tctx) { Context::Context(turbine::Context &tctx) noexcept: turbineCtx(tctx) {

View File

@ -63,19 +63,6 @@ ox::Error loadSpritePalette(
return {}; return {};
} }
void clearCbb(Context&, unsigned const cbb) noexcept {
for (auto &v : MEM_BG_TILES[cbb]) {
v = 0;
}
}
void clearCbbs(Context &ctx) noexcept {
clearCbb(ctx, 0);
clearCbb(ctx, 1);
clearCbb(ctx, 2);
clearCbb(ctx, 3);
}
static ox::Error loadTileSheetSet( static ox::Error loadTileSheetSet(
Context &ctx, Context &ctx,
ox::Span<uint16_t> tileMapTargetMem, ox::Span<uint16_t> tileMapTargetMem,
@ -112,10 +99,10 @@ ox::Error loadBgTileSheet(
size_t const tileCnt) noexcept { size_t const tileCnt) noexcept {
size_t const bppMod = ts.bpp == 4; size_t const bppMod = ts.bpp == 4;
size_t const bytesPerTile = PixelsPerTile >> bppMod; size_t const bytesPerTile = PixelsPerTile >> bppMod;
auto const cnt = (tileCnt * bytesPerTile) / 2; auto const pixCnt = tileCnt * bytesPerTile;
auto const srcPxIdx = srcTileIdx * bytesPerTile; auto const srcPxIdx = srcTileIdx * bytesPerTile;
auto const dstPxIdx = (dstTileIdx * bytesPerTile) / 2; auto const dstPxIdx = (dstTileIdx * bytesPerTile) / 2;
for (size_t i = 0; i < cnt; ++i) { for (size_t i = 0; i < pixCnt; ++i) {
auto const srcIdx = srcPxIdx + i * 2; auto const srcIdx = srcPxIdx + i * 2;
auto const p1 = static_cast<uint16_t>(ts.pixels[srcIdx]); auto const p1 = static_cast<uint16_t>(ts.pixels[srcIdx]);
auto const p2 = static_cast<uint16_t>(ts.pixels[srcIdx + 1]); auto const p2 = static_cast<uint16_t>(ts.pixels[srcIdx + 1]);

View File

@ -4,7 +4,6 @@
#include <ox/std/def.hpp> #include <ox/std/def.hpp>
#include <ox/std/realstd.hpp>
#include <keel/media.hpp> #include <keel/media.hpp>
#include <turbine/turbine.hpp> #include <turbine/turbine.hpp>
@ -49,10 +48,12 @@ OX_ALLOW_UNSAFE_BUFFERS_END
oxErrf("\tError Message:\t{}\n", err.msg); oxErrf("\tError Message:\t{}\n", err.msg);
} }
oxErrf("\tError Code:\t{}\n", static_cast<ErrorCode>(err)); oxErrf("\tError Code:\t{}\n", static_cast<ErrorCode>(err));
if (err.src.file_name() != nullptr) { if (err.file != nullptr) {
oxErrf("\tError Location:\t{}:{}\n", err.src.file_name(), err.src.line()); oxErrf("\tError Location:\t{}:{}\n", err.file, err.line);
} }
abort(); // disable all interrupt handling and IntrWait on no interrupts
REG_IE = 0;
teagba::intrwait(0, 0);
} }
} }

View File

@ -67,7 +67,7 @@ static class: public keel::Module {
ox::Vector<keel::PackTransform> packTransforms() const noexcept final { ox::Vector<keel::PackTransform> packTransforms() const noexcept final {
return { return {
// convert tilesheets to CompactTileSheets // convert tilesheets to CompactTileSheets
[](keel::Context &ctx, ox::Buffer &buff, ox::StringViewCR typeId) -> ox::Result<bool> { [](keel::Context &ctx, ox::Buffer &buff, ox::StringView typeId) -> ox::Result<bool> {
if (typeId == ox::ModelTypeId_v<TileSheetV1> || if (typeId == ox::ModelTypeId_v<TileSheetV1> ||
typeId == ox::ModelTypeId_v<TileSheetV2> || typeId == ox::ModelTypeId_v<TileSheetV2> ||
typeId == ox::ModelTypeId_v<TileSheetV3> || typeId == ox::ModelTypeId_v<TileSheetV3> ||
@ -78,7 +78,7 @@ static class: public keel::Module {
} }
return false; return false;
}, },
[](keel::Context &ctx, ox::Buffer &buff, ox::StringViewCR typeId) -> ox::Result<bool> { [](keel::Context &ctx, ox::Buffer &buff, ox::StringView typeId) -> ox::Result<bool> {
if (typeId == ox::ModelTypeId_v<NostalgiaPalette> || if (typeId == ox::ModelTypeId_v<NostalgiaPalette> ||
typeId == ox::ModelTypeId_v<PaletteV1> || typeId == ox::ModelTypeId_v<PaletteV1> ||
typeId == ox::ModelTypeId_v<PaletteV2> || typeId == ox::ModelTypeId_v<PaletteV2> ||

View File

@ -7,8 +7,8 @@
namespace nostalgia::core { namespace nostalgia::core {
void safeDelete(Context *ctx) noexcept { void ContextDeleter::operator()(Context *p) noexcept {
delete ctx; ox::safeDelete(p);
} }
Context::Context(turbine::Context &tctx, InitParams const&params) noexcept: Context::Context(turbine::Context &tctx, InitParams const&params) noexcept:

View File

@ -111,15 +111,15 @@ static constexpr auto bgVertexRow(uint_t x, uint_t y) noexcept {
} }
static void setSpriteBufferObject( static void setSpriteBufferObject(
uint_t const vi, uint_t vi,
float const enabled, float enabled,
float x, float x,
float y, float y,
uint_t const textureRow, uint_t textureRow,
uint_t const flipX, uint_t flipX,
uint_t const priority, uint_t priority,
ox::Span<float> const vbo, float *vbo,
ox::Span<GLuint> const ebo) noexcept { GLuint *ebo) noexcept {
// don't worry, this memcpy gets optimized to something much more ideal // don't worry, this memcpy gets optimized to something much more ideal
constexpr float xmod = 0.1f; constexpr float xmod = 0.1f;
constexpr float ymod = 0.1f; constexpr float ymod = 0.1f;
@ -138,25 +138,25 @@ static void setSpriteBufferObject(
enabled, x + xmod, y + ymod, prif, R, textureRowf + 0, // top right enabled, x + xmod, y + ymod, prif, R, textureRowf + 0, // top right
enabled, x, y + ymod, prif, L, textureRowf + 0, // top left enabled, x, y + ymod, prif, L, textureRowf + 0, // top left
}; };
ox::spancpy<float>(vbo, vertices); memcpy(vbo, vertices.data(), sizeof(vertices));
ox::Array<GLuint, SpriteVertexEboLength> const elms { ox::Array<GLuint, SpriteVertexEboLength> const elms {
vi + 0, vi + 1, vi + 2, vi + 0, vi + 1, vi + 2,
vi + 2, vi + 3, vi + 0, vi + 2, vi + 3, vi + 0,
}; };
ox::spancpy<GLuint>(ebo, elms); memcpy(ebo, elms.data(), sizeof(elms));
} }
static void setTileBufferObject( static void setTileBufferObject(
uint_t const vi, uint_t vi,
float x, float x,
float y, float y,
float const textureTileIdx, float textureTileIdx,
float const priority, float priority,
float const palOffset, float palOffset,
bool const flipX, bool flipX,
bool const flipY, bool flipY,
ox::Span<float> const vbo, float *vbo,
ox::Span<GLuint> const ebo) noexcept { GLuint *ebo) noexcept {
// don't worry, this memcpy gets optimized to something much more ideal // don't worry, this memcpy gets optimized to something much more ideal
constexpr float ymod = 0.1f; constexpr float ymod = 0.1f;
constexpr float xmod = 0.1f; constexpr float xmod = 0.1f;
@ -170,35 +170,24 @@ static void setTileBufferObject(
float const T = flipY ? 1 : 0; float const T = flipY ? 1 : 0;
float const B = flipY ? 0 : 1; float const B = flipY ? 0 : 1;
ox::Array<float, BgVertexVboLength> const vertices { ox::Array<float, BgVertexVboLength> const vertices {
x, y, prif, L, B, textureTileIdx, palOffset, // bottom left x, y, prif, L, B, textureTileIdx, palOffset, // bottom left
x + xmod, y, prif, R, B, textureTileIdx, palOffset, // bottom right x + xmod, y, prif, R, B, textureTileIdx, palOffset, // bottom right
x + xmod, y + ymod, prif, R, T, textureTileIdx, palOffset, // top right x + xmod, y + ymod, prif, R, T, textureTileIdx, palOffset, // top right
x, y + ymod, prif, L, T, textureTileIdx, palOffset, // top left x, y + ymod, prif, L, T, textureTileIdx, palOffset, // top left
}; };
ox::spancpy<float>(vbo, vertices); memcpy(vbo, vertices.data(), sizeof(vertices));
ox::Array<GLuint, BgVertexEboLength> const elms { ox::Array<GLuint, BgVertexEboLength> const elms {
vi + 0, vi + 1, vi + 2, vi + 0, vi + 1, vi + 2,
vi + 2, vi + 3, vi + 0, vi + 2, vi + 3, vi + 0,
}; };
ox::spancpy<GLuint>(ebo, elms); memcpy(ebo, elms.data(), sizeof(elms));
} }
static void initSpriteBufferObjects(Context &ctx, glutils::BufferSet &bs) noexcept { static void initSpriteBufferObjects(Context &ctx, glutils::BufferSet &bs) noexcept {
for (auto i = 0u; i < ctx.spriteCount; ++i) { for (auto i = 0u; i < ctx.spriteCount; ++i) {
auto const vbo = ox::Span{bs.vertices} auto vbo = &bs.vertices[i * static_cast<std::size_t>(SpriteVertexVboLength)];
+ i * static_cast<std::size_t>(SpriteVertexVboLength); auto ebo = &bs.elements[i * static_cast<std::size_t>(SpriteVertexEboLength)];
auto const ebo = ox::Span{bs.elements} setSpriteBufferObject(i * static_cast<uint_t>(SpriteVertexVboRows) * ctx.blocksPerSprite, 0, 0, 0, 0, false, 0, vbo, ebo);
+ i * static_cast<std::size_t>(SpriteVertexEboLength);
setSpriteBufferObject(
i * static_cast<uint_t>(SpriteVertexVboRows) * ctx.blocksPerSprite,
0,
0,
0,
0,
false,
0,
vbo,
ebo);
} }
} }
@ -206,10 +195,8 @@ static void initBackgroundBufferObjects(glutils::BufferSet &bs) noexcept {
for (auto x = 0u; x < TileColumns; ++x) { for (auto x = 0u; x < TileColumns; ++x) {
for (auto y = 0u; y < TileRows; ++y) { for (auto y = 0u; y < TileRows; ++y) {
const auto i = bgVertexRow(x, y); const auto i = bgVertexRow(x, y);
auto const vbo = ox::Span{bs.vertices} auto vbo = &bs.vertices[i * static_cast<std::size_t>(BgVertexVboLength)];
+ i * static_cast<std::size_t>(BgVertexVboLength); auto ebo = &bs.elements[i * static_cast<std::size_t>(BgVertexEboLength)];
auto const ebo = ox::Span{bs.elements}
+ i * static_cast<std::size_t>(BgVertexEboLength);
setTileBufferObject( setTileBufferObject(
static_cast<uint_t>(i * BgVertexVboRows), static_cast<uint_t>(i * BgVertexVboRows),
static_cast<float>(x), static_cast<float>(x),
@ -434,8 +421,8 @@ static void setSprite(
auto const eboIdx = eboBase + renderer::SpriteVertexEboLength * i; auto const eboIdx = eboBase + renderer::SpriteVertexEboLength * i;
oxAssert(vboIdx < ctx.spriteBlocks.vertices.size(), "vbo overflow"); oxAssert(vboIdx < ctx.spriteBlocks.vertices.size(), "vbo overflow");
oxAssert(eboIdx < ctx.spriteBlocks.elements.size(), "ebo overflow"); oxAssert(eboIdx < ctx.spriteBlocks.elements.size(), "ebo overflow");
auto const vbo = ox::Span{ctx.spriteBlocks.vertices} + vboIdx; auto const vbo = &ctx.spriteBlocks.vertices[vboIdx];
auto const ebo = ox::Span{ctx.spriteBlocks.elements} + eboIdx; auto const ebo = &ctx.spriteBlocks.elements[eboIdx];
renderer::setSpriteBufferObject( renderer::setSpriteBufferObject(
static_cast<uint_t>(vboIdx), static_cast<uint_t>(vboIdx),
enabled, enabled,
@ -569,7 +556,7 @@ static void copyPixels(
CompactTileSheet const&ts, CompactTileSheet const&ts,
ox::Span<uint32_t> dst, ox::Span<uint32_t> dst,
size_t const srcPxIdx, size_t const srcPxIdx,
size_t const pxlCnt) noexcept { size_t pxlCnt) noexcept {
size_t idx{}; size_t idx{};
if (ts.bpp == 4) { if (ts.bpp == 4) {
for (size_t i = 0; i < pxlCnt; i += 2) { for (size_t i = 0; i < pxlCnt; i += 2) {
@ -586,18 +573,6 @@ static void copyPixels(
} }
} }
void clearCbb(Context &ctx, unsigned const cbb) noexcept {
for (auto &v : ctx.cbbs[cbb].pixels) {
v = 0;
}
}
void clearCbbs(Context &ctx) noexcept {
for (unsigned i = 0 ; i < ctx.cbbs.size(); ++i) {
clearCbb(ctx, i);
}
}
ox::Error loadBgTileSheet( ox::Error loadBgTileSheet(
Context &ctx, Context &ctx,
unsigned const cbb, unsigned const cbb,
@ -681,8 +656,8 @@ void setBgTile(
const auto x = static_cast<uint_t>(column); const auto x = static_cast<uint_t>(column);
const auto i = renderer::bgVertexRow(x, y); const auto i = renderer::bgVertexRow(x, y);
auto &cbb = ctx.cbbs[z]; auto &cbb = ctx.cbbs[z];
const auto vbo = ox::Span{cbb.vertices} + i * renderer::BgVertexVboLength; const auto vbo = &cbb.vertices[i * renderer::BgVertexVboLength];
const auto ebo = ox::Span{cbb.elements} + i * renderer::BgVertexEboLength; const auto ebo = &cbb.elements[i * renderer::BgVertexEboLength];
auto &bg = ctx.backgrounds[bgIdx]; auto &bg = ctx.backgrounds[bgIdx];
renderer::setTileBufferObject( renderer::setTileBufferObject(
static_cast<uint_t>(i * renderer::BgVertexVboRows), static_cast<uint_t>(i * renderer::BgVertexVboRows),

View File

@ -3,6 +3,7 @@ add_library(NostalgiaCore-Studio)
add_library( add_library(
NostalgiaCore-Studio-ImGui NostalgiaCore-Studio-ImGui
studiomodule.cpp studiomodule.cpp
tilesheeteditor/tilesheeteditor-imgui.cpp
) )
target_link_libraries( target_link_libraries(
@ -14,6 +15,7 @@ target_link_libraries(
target_link_libraries( target_link_libraries(
NostalgiaCore-Studio-ImGui PUBLIC NostalgiaCore-Studio-ImGui PUBLIC
NostalgiaCore-Studio NostalgiaCore-Studio
Studio
) )
install( install(

View File

@ -1,7 +1,6 @@
target_sources( target_sources(
NostalgiaCore-Studio PRIVATE NostalgiaCore-Studio PRIVATE
commands/addcolorcommand.cpp commands/addcolorcommand.cpp
commands/addpagecommand.cpp
commands/applycolorallpagescommand.cpp commands/applycolorallpagescommand.cpp
commands/duplicatepagecommand.cpp commands/duplicatepagecommand.cpp
commands/movecolorcommand.cpp commands/movecolorcommand.cpp
@ -10,9 +9,5 @@ target_sources(
commands/renamepagecommand.cpp commands/renamepagecommand.cpp
commands/updatecolorcommand.cpp commands/updatecolorcommand.cpp
commands/updatecolorinfocommand.cpp commands/updatecolorinfocommand.cpp
)
target_sources(
NostalgiaCore-Studio-ImGui PRIVATE
paletteeditor-imgui.cpp paletteeditor-imgui.cpp
) )

View File

@ -1,28 +0,0 @@
/*
* 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 {};
}
}

View File

@ -1,31 +0,0 @@
/*
* 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;
};
}

View File

@ -9,7 +9,6 @@ namespace nostalgia::core {
enum class PaletteEditorCommandId { enum class PaletteEditorCommandId {
ApplyColorAllPages, ApplyColorAllPages,
RenamePage, RenamePage,
AddPage,
DuplicatePage, DuplicatePage,
RemovePage, RemovePage,
AddColor, AddColor,

View File

@ -23,7 +23,7 @@ int DuplicatePageCommand::commandId() const noexcept {
} }
ox::Error DuplicatePageCommand::redo() noexcept { ox::Error DuplicatePageCommand::redo() noexcept {
m_pal.pages.emplace(m_dstIdx, ox::sfmt("Page {}", m_pal.pages.size() + 1), std::move(m_page)); m_pal.pages.emplace(m_dstIdx, "", std::move(m_page));
return {}; return {};
} }

View File

@ -7,7 +7,6 @@
#include <keel/media.hpp> #include <keel/media.hpp>
#include "commands/addcolorcommand.hpp" #include "commands/addcolorcommand.hpp"
#include "commands/addpagecommand.hpp"
#include "commands/applycolorallpagescommand.hpp" #include "commands/applycolorallpagescommand.hpp"
#include "commands/duplicatepagecommand.hpp" #include "commands/duplicatepagecommand.hpp"
#include "commands/movecolorcommand.hpp" #include "commands/movecolorcommand.hpp"
@ -197,11 +196,7 @@ void PaletteEditorImGui::drawPagesEditor() noexcept {
constexpr auto toolbarHeight = 40; constexpr auto toolbarHeight = 40;
auto const btnSz = ImVec2{paneSz.x / 4 - 5.5f, 24}; auto const btnSz = ImVec2{paneSz.x / 4 - 5.5f, 24};
if (ImGui::Button("Add", btnSz)) { if (ImGui::Button("Add", btnSz)) {
if (m_pal.pages.empty()) { std::ignore = pushCommand<DuplicatePageCommand>(m_pal, 0u, m_pal.pages.size());
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; m_page = m_pal.pages.size() - 1;
} }
ImGui::SameLine(); ImGui::SameLine();

View File

@ -6,11 +6,6 @@ target_sources(
tilesheetpixels.cpp tilesheetpixels.cpp
) )
target_sources(
NostalgiaCore-Studio-ImGui PRIVATE
tilesheeteditor-imgui.cpp
)
target_link_libraries( target_link_libraries(
NostalgiaCore-Studio-ImGui PUBLIC NostalgiaCore-Studio-ImGui PUBLIC
lodepng lodepng

View File

@ -9,24 +9,27 @@ namespace nostalgia::core {
core::UpdateSubSheetCommand::UpdateSubSheetCommand( core::UpdateSubSheetCommand::UpdateSubSheetCommand(
TileSheet &img, TileSheet &img,
TileSheet::SubSheetIdx idx, TileSheet::SubSheetIdx idx,
ox::StringParam name, ox::String name,
int const cols, int cols,
int const rows): int rows) noexcept:
m_img{img}, m_img(img),
m_idx{std::move(idx)}, m_idx(std::move(idx)),
m_sheet{getSubSheet(m_img, m_idx)} { m_sheet(getSubSheet(m_img, m_idx)),
m_sheet = getSubSheet(m_img, m_idx); m_newName(std::move(name)),
m_sheet.name = std::move(name); m_newCols(cols),
OX_THROW_ERROR(resizeSubsheet(m_sheet, m_img.bpp, {cols, rows})); m_newRows(rows) {
} }
ox::Error UpdateSubSheetCommand::redo() noexcept { ox::Error UpdateSubSheetCommand::redo() noexcept {
std::swap(m_sheet, getSubSheet(m_img, m_idx)); auto &sheet = getSubSheet(m_img, m_idx);
sheet.name = m_newName;
oxLogError(resizeSubsheet(sheet, m_img.bpp, {m_newCols, m_newRows}));
return {}; return {};
} }
ox::Error UpdateSubSheetCommand::undo() noexcept { ox::Error UpdateSubSheetCommand::undo() noexcept {
std::swap(m_sheet, getSubSheet(m_img, m_idx)); auto &sheet = getSubSheet(m_img, m_idx);
sheet = m_sheet;
return {}; return {};
} }

View File

@ -13,14 +13,17 @@ class UpdateSubSheetCommand: public TileSheetCommand {
TileSheet &m_img; TileSheet &m_img;
TileSheet::SubSheetIdx m_idx; TileSheet::SubSheetIdx m_idx;
TileSheet::SubSheet m_sheet; TileSheet::SubSheet m_sheet;
ox::String m_newName;
int m_newCols = 0;
int m_newRows = 0;
public: public:
UpdateSubSheetCommand( UpdateSubSheetCommand(
TileSheet &img, TileSheet &img,
TileSheet::SubSheetIdx idx, TileSheet::SubSheetIdx idx,
ox::StringParam name, ox::String name,
int cols, int cols,
int rows); int rows) noexcept;
ox::Error redo() noexcept final; ox::Error redo() noexcept final;

View File

@ -234,7 +234,7 @@ void TileSheetEditorImGui::draw(studio::StudioContext&) noexcept {
ImGui::BeginChild("SubSheets", {s_palViewWidth - 24, ySize / 2.f}, true); ImGui::BeginChild("SubSheets", {s_palViewWidth - 24, ySize / 2.f}, true);
{ {
static constexpr auto btnHeight = ig::BtnSz.y; static constexpr auto btnHeight = ig::BtnSz.y;
auto constexpr btnSize = ImVec2{btnHeight, btnHeight}; auto const btnSize = ImVec2{btnHeight, btnHeight};
if (ig::PushButton("+", btnSize)) { if (ig::PushButton("+", btnSize)) {
auto insertOnIdx = m_model.activeSubSheetIdx(); auto insertOnIdx = m_model.activeSubSheetIdx();
auto const&parent = m_model.activeSubSheet(); auto const&parent = m_model.activeSubSheet();
@ -258,19 +258,16 @@ void TileSheetEditorImGui::draw(studio::StudioContext&) noexcept {
m_exportMenu.show(); m_exportMenu.show();
} }
TileSheet::SubSheetIdx path; TileSheet::SubSheetIdx path;
static constexpr auto flags = static constexpr auto flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody;
ImGuiTableFlags_RowBg | if (ImGui::BeginTable("Subsheets", 4, flags)) {
ImGuiTableFlags_NoBordersInBody | ImGui::TableSetupColumn("Subsheet", ImGuiTableColumnFlags_NoHide);
ImGuiTableFlags_ScrollY; ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 25);
if (ImGui::BeginTable("Subsheets", 4, flags)) { ImGui::TableSetupColumn("Columns", ImGuiTableColumnFlags_WidthFixed, 50);
ImGui::TableSetupColumn("Subsheet", ImGuiTableColumnFlags_NoHide); ImGui::TableSetupColumn("Rows", ImGuiTableColumnFlags_WidthFixed, 50);
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 25); ImGui::TableHeadersRow();
ImGui::TableSetupColumn("Columns", ImGuiTableColumnFlags_WidthFixed, 50); drawSubsheetSelector(m_view.img().subsheet, path);
ImGui::TableSetupColumn("Rows", ImGuiTableColumnFlags_WidthFixed, 50); ImGui::EndTable();
ImGui::TableHeadersRow(); }
drawSubsheetSelector(m_view.img().subsheet, path);
ImGui::EndTable();
}
} }
ImGui::EndChild(); ImGui::EndChild();
} }
@ -465,12 +462,8 @@ void TileSheetEditorImGui::drawPaletteMenu() noexcept {
} }
} }
// header // header
auto constexpr palTblFlags =
ImGuiTableFlags_RowBg |
ImGuiTableFlags_SizingStretchProp |
ImGuiTableFlags_ScrollY;
if (ImGui::BeginTable( if (ImGui::BeginTable(
"PaletteTable", 4, palTblFlags)) { "PaletteTable", 4, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingStretchProp)) {
ImGui::TableSetupColumn("Idx", 0, 0.6f); ImGui::TableSetupColumn("Idx", 0, 0.6f);
ImGui::TableSetupColumn("", 0, 0.22f); ImGui::TableSetupColumn("", 0, 0.22f);
ImGui::TableSetupColumn("Name", 0, 3); ImGui::TableSetupColumn("Name", 0, 3);

View File

@ -26,6 +26,11 @@
namespace nostalgia::core { namespace nostalgia::core {
Palette const TileSheetEditorModel::s_defaultPalette = {
.colorNames = {ox::Vector<ox::String>{{}}},
.pages = {{"Page 1", ox::Vector<Color16>(128)}},
};
// delete pixels of all non-leaf nodes // delete pixels of all non-leaf nodes
static void normalizeSubsheets(TileSheet::SubSheet &ss) noexcept { static void normalizeSubsheets(TileSheet::SubSheet &ss) noexcept {
if (ss.subsheets.empty()) { if (ss.subsheets.empty()) {
@ -37,14 +42,7 @@ static void normalizeSubsheets(TileSheet::SubSheet &ss) noexcept {
} }
} }
TileSheetEditorModel::TileSheetEditorModel(studio::StudioContext &sctx, ox::StringView path, studio::UndoStack &undoStack):
Palette const TileSheetEditorModel::s_defaultPalette = {
.colorNames = {ox::Vector<ox::String>{{}}},
.pages = {{"Page 1", ox::Vector<Color16>(128)}},
};
TileSheetEditorModel::TileSheetEditorModel(
studio::StudioContext &sctx, ox::StringViewCR path, studio::UndoStack &undoStack):
m_sctx(sctx), m_sctx(sctx),
m_tctx(m_sctx.tctx), m_tctx(m_sctx.tctx),
m_path(path), m_path(path),
@ -64,7 +62,7 @@ void TileSheetEditorModel::cut() {
TileSheetClipboard blankCb; TileSheetClipboard blankCb;
auto cb = ox::make_unique<TileSheetClipboard>(); auto cb = ox::make_unique<TileSheetClipboard>();
auto const&s = activeSubSheet(); auto const&s = activeSubSheet();
iterateSelectionRows(*m_selection, [&](int const x, int const y) { iterateSelectionRows(*m_selection, [&](int x, int y) {
auto pt = ox::Point{x, y}; auto pt = ox::Point{x, y};
auto const idx = core::idx(s, pt); auto const idx = core::idx(s, pt);
auto const c = getPixel(s, m_img.bpp, idx); auto const c = getPixel(s, m_img.bpp, idx);
@ -75,8 +73,7 @@ void TileSheetEditorModel::cut() {
auto const pt1 = m_selection->a; auto const pt1 = m_selection->a;
auto const pt2 = ox::Point{s.columns * TileWidth, s.rows * TileHeight}; auto const pt2 = ox::Point{s.columns * TileWidth, s.rows * TileHeight};
turbine::setClipboardObject(m_tctx, std::move(cb)); turbine::setClipboardObject(m_tctx, std::move(cb));
pushCommand(ox::make<CutPasteCommand>( pushCommand(ox::make<CutPasteCommand>(CommandId::Cut, m_img, m_activeSubsSheetIdx, pt1, pt2, blankCb));
CommandId::Cut, m_img, m_activeSubsSheetIdx, pt1, pt2, blankCb));
} }
void TileSheetEditorModel::copy() { void TileSheetEditorModel::copy() {
@ -84,7 +81,7 @@ void TileSheetEditorModel::copy() {
return; return;
} }
auto cb = ox::make_unique<TileSheetClipboard>(); auto cb = ox::make_unique<TileSheetClipboard>();
iterateSelectionRows(*m_selection, [&](int const x, int const y) { iterateSelectionRows(*m_selection, [&](int x, int y) {
auto pt = ox::Point{x, y}; auto pt = ox::Point{x, y};
const auto&s = activeSubSheet(); const auto&s = activeSubSheet();
const auto idx = core::idx(s, pt); const auto idx = core::idx(s, pt);
@ -108,8 +105,7 @@ void TileSheetEditorModel::paste() {
auto const&s = activeSubSheet(); auto const&s = activeSubSheet();
auto const pt1 = m_selection->a; auto const pt1 = m_selection->a;
auto const pt2 = ox::Point{s.columns * TileWidth, s.rows * TileHeight}; auto const pt2 = ox::Point{s.columns * TileWidth, s.rows * TileHeight};
pushCommand(ox::make<CutPasteCommand>( pushCommand(ox::make<CutPasteCommand>(CommandId::Paste, m_img, m_activeSubsSheetIdx, pt1, pt2, *cb));
CommandId::Paste, m_img, m_activeSubsSheetIdx, pt1, pt2, *cb));
} }
bool TileSheetEditorModel::acceptsClipboardPayload() const noexcept { bool TileSheetEditorModel::acceptsClipboardPayload() const noexcept {
@ -124,8 +120,8 @@ ox::StringView TileSheetEditorModel::palPath() const noexcept {
} }
constexpr ox::StringView uuidPrefix = "uuid://"; constexpr ox::StringView uuidPrefix = "uuid://";
if (ox::beginsWith(path, uuidPrefix)) { if (ox::beginsWith(path, uuidPrefix)) {
auto const uuid = ox::StringView(&path[uuidPrefix.bytes()], path.bytes() - uuidPrefix.bytes()); auto uuid = ox::StringView(&path[uuidPrefix.bytes()], path.bytes() - uuidPrefix.bytes());
auto const out = keelCtx(m_tctx).uuidToPath.at(uuid); auto out = keelCtx(m_tctx).uuidToPath.at(uuid);
if (out.error) { if (out.error) {
return {}; return {};
} }
@ -135,14 +131,13 @@ ox::StringView TileSheetEditorModel::palPath() const noexcept {
} }
} }
ox::Error TileSheetEditorModel::setPalette(ox::StringViewCR path) noexcept { ox::Error TileSheetEditorModel::setPalette(ox::StringView path) noexcept {
OX_REQUIRE(uuid, keelCtx(m_tctx).pathToUuid.at(path)); OX_REQUIRE(uuid, keelCtx(m_tctx).pathToUuid.at(path));
pushCommand(ox::make<PaletteChangeCommand>( pushCommand(ox::make<PaletteChangeCommand>(activeSubSheetIdx(), m_img, uuid->toString()));
activeSubSheetIdx(), m_img, uuid->toString()));
return {}; return {};
} }
void TileSheetEditorModel::setPalettePage(size_t const pg) noexcept { void TileSheetEditorModel::setPalettePage(size_t pg) noexcept {
m_palettePage = ox::clamp<size_t>(pg, 0, m_pal->pages.size() - 1); m_palettePage = ox::clamp<size_t>(pg, 0, m_pal->pages.size() - 1);
m_updated = true; m_updated = true;
} }
@ -151,7 +146,7 @@ size_t TileSheetEditorModel::palettePage() const noexcept {
return m_palettePage; return m_palettePage;
} }
void TileSheetEditorModel::drawCommand(ox::Point const&pt, std::size_t const palIdx) noexcept { void TileSheetEditorModel::drawCommand(ox::Point const&pt, std::size_t palIdx) noexcept {
const auto &activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx); const auto &activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx);
if (pt.x >= activeSubSheet.columns * TileWidth || pt.y >= activeSubSheet.rows * TileHeight) { if (pt.x >= activeSubSheet.columns * TileWidth || pt.y >= activeSubSheet.rows * TileHeight) {
return; return;
@ -160,8 +155,7 @@ void TileSheetEditorModel::drawCommand(ox::Point const&pt, std::size_t const pal
if (m_ongoingDrawCommand) { if (m_ongoingDrawCommand) {
m_updated = m_updated || m_ongoingDrawCommand->append(idx); m_updated = m_updated || m_ongoingDrawCommand->append(idx);
} else if (getPixel(activeSubSheet, m_img.bpp, idx) != palIdx) { } else if (getPixel(activeSubSheet, m_img.bpp, idx) != palIdx) {
pushCommand(ox::make<DrawCommand>( pushCommand(ox::make<DrawCommand>(m_img, m_activeSubsSheetIdx, idx, static_cast<int>(palIdx)));
m_img, m_activeSubsSheetIdx, idx, static_cast<int>(palIdx)));
} }
} }
@ -177,20 +171,16 @@ void TileSheetEditorModel::rmSubsheet(TileSheet::SubSheetIdx const&idx) noexcept
pushCommand(ox::make<RmSubSheetCommand>(m_img, idx)); pushCommand(ox::make<RmSubSheetCommand>(m_img, idx));
} }
void TileSheetEditorModel::insertTiles( void TileSheetEditorModel::insertTiles(TileSheet::SubSheetIdx const&idx, std::size_t tileIdx, std::size_t tileCnt) noexcept {
TileSheet::SubSheetIdx const&idx, std::size_t const tileIdx, std::size_t const tileCnt) noexcept {
pushCommand(ox::make<InsertTilesCommand>(m_img, idx, tileIdx, tileCnt)); pushCommand(ox::make<InsertTilesCommand>(m_img, idx, tileIdx, tileCnt));
} }
void TileSheetEditorModel::deleteTiles( void TileSheetEditorModel::deleteTiles(TileSheet::SubSheetIdx const&idx, std::size_t tileIdx, std::size_t tileCnt) noexcept {
TileSheet::SubSheetIdx const&idx, std::size_t const tileIdx, std::size_t const tileCnt) noexcept {
pushCommand(ox::make<DeleteTilesCommand>(m_img, idx, tileIdx, tileCnt)); pushCommand(ox::make<DeleteTilesCommand>(m_img, idx, tileIdx, tileCnt));
} }
ox::Error TileSheetEditorModel::updateSubsheet( ox::Error TileSheetEditorModel::updateSubsheet(TileSheet::SubSheetIdx const&idx, ox::StringView const&name, int cols, int rows) noexcept {
TileSheet::SubSheetIdx const&idx, ox::StringViewCR name, int const cols, int const rows) noexcept { pushCommand(ox::make<UpdateSubSheetCommand>(m_img, idx, ox::String(name), cols, rows));
OX_REQUIRE(cmd, ox::makeCatch<UpdateSubSheetCommand>(m_img, idx, name, cols, rows));
pushCommand(cmd);
return {}; return {};
} }
@ -199,7 +189,7 @@ void TileSheetEditorModel::setActiveSubsheet(TileSheet::SubSheetIdx const&idx) n
this->activeSubsheetChanged.emit(m_activeSubsSheetIdx); this->activeSubsheetChanged.emit(m_activeSubsSheetIdx);
} }
void TileSheetEditorModel::fill(ox::Point const&pt, int const palIdx) noexcept { void TileSheetEditorModel::fill(ox::Point const&pt, int palIdx) noexcept {
auto const&activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx); auto const&activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx);
// build idx list // build idx list
if (pt.x >= activeSubSheet.columns * TileWidth || pt.y >= activeSubSheet.rows * TileHeight) { if (pt.x >= activeSubSheet.columns * TileWidth || pt.y >= activeSubSheet.rows * TileHeight) {
@ -207,10 +197,10 @@ void TileSheetEditorModel::fill(ox::Point const&pt, int const palIdx) noexcept {
} }
ox::Array<bool, PixelsPerTile> updateMap = {}; ox::Array<bool, PixelsPerTile> updateMap = {};
auto const oldColor = getPixel(activeSubSheet, m_img.bpp, pt); auto const oldColor = getPixel(activeSubSheet, m_img.bpp, pt);
getFillPixels(activeSubSheet, updateMap, pt, oldColor); getFillPixels(updateMap, pt, oldColor);
ox::Vector<std::size_t> idxList; ox::Vector<std::size_t> idxList;
auto i = core::idx(activeSubSheet, pt) / PixelsPerTile * PixelsPerTile; auto i = core::idx(activeSubSheet, pt) / PixelsPerTile * PixelsPerTile;
for (auto const u : updateMap) { for (auto u : updateMap) {
if (u) { if (u) {
idxList.emplace_back(i); idxList.emplace_back(i);
} }
@ -240,7 +230,7 @@ void TileSheetEditorModel::completeSelection() noexcept {
m_selTracker.finishSelection(); m_selTracker.finishSelection();
m_selection.emplace(m_selTracker.selection()); m_selection.emplace(m_selTracker.selection());
auto&pt = m_selection->b; auto&pt = m_selection->b;
auto const&s = activeSubSheet(); auto&s = activeSubSheet();
pt.x = ox::min(s.columns * TileWidth - 1, pt.x); pt.x = ox::min(s.columns * TileWidth - 1, pt.x);
pt.y = ox::min(s.rows * TileHeight - 1, pt.y); pt.y = ox::min(s.rows * TileHeight - 1, pt.y);
} }
@ -285,44 +275,47 @@ ox::Error TileSheetEditorModel::saveFile() noexcept {
return m_sctx.project->writeObj(m_path, m_img, ox::ClawFormat::Metal); return m_sctx.project->writeObj(m_path, m_img, ox::ClawFormat::Metal);
} }
bool TileSheetEditorModel::pixelSelected(std::size_t const idx) const noexcept { bool TileSheetEditorModel::pixelSelected(std::size_t idx) const noexcept {
auto const&s = activeSubSheet(); auto const&s = activeSubSheet();
auto const pt = idxToPt(static_cast<int>(idx), s.columns); auto const pt = idxToPt(static_cast<int>(idx), s.columns);
return m_selection && m_selection->contains(pt); return m_selection && m_selection->contains(pt);
} }
void TileSheetEditorModel::getFillPixels( void TileSheetEditorModel::getFillPixels(ox::Span<bool> pixels, ox::Point const&pt, int oldColor) const noexcept {
TileSheet::SubSheet const&activeSubSheet, const auto &activeSubSheet = this->activeSubSheet();
ox::Span<bool> pixels, const auto tileIdx = [activeSubSheet](const ox::Point &pt) noexcept {
ox::Point const&pt, return ptToIdx(pt, activeSubSheet.columns) / PixelsPerTile;
int const oldColor) const noexcept { };
auto const idx = ptToIdx(pt, activeSubSheet.columns); // get points
auto const relIdx = idx % PixelsPerTile; const auto leftPt = pt + ox::Point(-1, 0);
if (pixels[relIdx] || getPixel(activeSubSheet, m_img.bpp, idx) != oldColor) { const auto rightPt = pt + ox::Point(1, 0);
return; const auto topPt = pt + ox::Point(0, -1);
} const auto bottomPt = pt + ox::Point(0, 1);
// calculate indices
const auto idx = ptToIdx(pt, activeSubSheet.columns);
const auto leftIdx = ptToIdx(leftPt, activeSubSheet.columns);
const auto rightIdx = ptToIdx(rightPt, activeSubSheet.columns);
const auto topIdx = ptToIdx(topPt, activeSubSheet.columns);
const auto bottomIdx = ptToIdx(bottomPt, activeSubSheet.columns);
const auto tile = tileIdx(pt);
// mark pixels to update // mark pixels to update
pixels[relIdx] = true; pixels[idx % PixelsPerTile] = true;
if (pt.x % TileWidth != 0) { if (!pixels[leftIdx % PixelsPerTile] && tile == tileIdx(leftPt) && getPixel(activeSubSheet, m_img.bpp, leftIdx) == oldColor) {
auto const leftPt = pt + ox::Point{-1, 0}; getFillPixels(pixels, leftPt, oldColor);
getFillPixels(activeSubSheet, pixels, leftPt, oldColor);
} }
if (pt.x % TileWidth != TileWidth - 1) { if (!pixels[rightIdx % PixelsPerTile] && tile == tileIdx(rightPt) && getPixel(activeSubSheet, m_img.bpp, rightIdx) == oldColor) {
auto const rightPt = pt + ox::Point{1, 0}; getFillPixels(pixels, rightPt, oldColor);
getFillPixels(activeSubSheet, pixels, rightPt, oldColor);
} }
if (pt.y % TileHeight != 0) { if (!pixels[topIdx % PixelsPerTile] && tile == tileIdx(topPt) && getPixel(activeSubSheet, m_img.bpp, topIdx) == oldColor) {
auto const topPt = pt + ox::Point{0, -1}; getFillPixels(pixels, topPt, oldColor);
getFillPixels(activeSubSheet, pixels, topPt, oldColor);
} }
if (pt.y % TileHeight != TileHeight - 1) { if (!pixels[bottomIdx % PixelsPerTile] && tile == tileIdx(bottomPt) && getPixel(activeSubSheet, m_img.bpp, bottomIdx) == oldColor) {
auto const bottomPt = pt + ox::Point{0, 1}; getFillPixels(pixels, bottomPt, oldColor);
getFillPixels(activeSubSheet, pixels, bottomPt, oldColor);
} }
} }
void TileSheetEditorModel::pushCommand(studio::UndoCommand *cmd) noexcept { void TileSheetEditorModel::pushCommand(studio::UndoCommand *cmd) noexcept {
std::ignore = m_undoStack.push(ox::UPtr<studio::UndoCommand>{cmd}); std::ignore = m_undoStack.push(ox::UPtr<studio::UndoCommand>(cmd));
m_ongoingDrawCommand = dynamic_cast<DrawCommand*>(cmd); m_ongoingDrawCommand = dynamic_cast<DrawCommand*>(cmd);
m_updated = true; m_updated = true;
} }

View File

@ -4,7 +4,9 @@
#pragma once #pragma once
#include <ox/std/bounds.hpp>
#include <ox/std/point.hpp> #include <ox/std/point.hpp>
#include <ox/std/trace.hpp>
#include <ox/std/string.hpp> #include <ox/std/string.hpp>
#include <studio/studio.hpp> #include <studio/studio.hpp>
@ -36,7 +38,7 @@ class TileSheetEditorModel: public ox::SignalHandler {
bool m_updated = false; bool m_updated = false;
public: public:
TileSheetEditorModel(studio::StudioContext &sctx, ox::StringViewCR path, studio::UndoStack &undoStack); TileSheetEditorModel(studio::StudioContext &sctx, ox::StringView path, studio::UndoStack &undoStack);
~TileSheetEditorModel() override = default; ~TileSheetEditorModel() override = default;
@ -61,7 +63,7 @@ class TileSheetEditorModel: public ox::SignalHandler {
[[nodiscard]] [[nodiscard]]
ox::StringView palPath() const noexcept; ox::StringView palPath() const noexcept;
ox::Error setPalette(ox::StringViewCR path) noexcept; ox::Error setPalette(ox::StringView path) noexcept;
void setPalettePage(size_t pg) noexcept; void setPalettePage(size_t pg) noexcept;
@ -126,11 +128,7 @@ class TileSheetEditorModel: public ox::SignalHandler {
bool pixelSelected(std::size_t idx) const noexcept; bool pixelSelected(std::size_t idx) const noexcept;
private: private:
void getFillPixels( void getFillPixels(ox::Span<bool> pixels, ox::Point const&pt, int oldColor) const noexcept;
TileSheet::SubSheet const&activeSubSheet,
ox::Span<bool> pixels,
ox::Point const&pt,
int oldColor) const noexcept;
void pushCommand(studio::UndoCommand *cmd) noexcept; void pushCommand(studio::UndoCommand *cmd) noexcept;

View File

@ -66,10 +66,10 @@ static ox::Optional<size_t> getPixelIdx(
return ox::Optional<size_t>{}; return ox::Optional<size_t>{};
} }
ox::Optional<size_t> getTileIdx(TileSheet const&ts, SubSheetId const id) noexcept { size_t getTileIdx(TileSheet const&ts, SubSheetId const id) noexcept {
size_t idx{}; size_t idx{};
auto const out = getPixelIdx(ts.subsheet, id, idx, ts.bpp); auto const out = getPixelIdx(ts.subsheet, id, idx, ts.bpp);
return out ? ox::Optional<size_t>{ox::in_place, *out / PixelsPerTile} : out; return out.or_value(0) / PixelsPerTile;
} }
uint8_t getPixel4Bpp(TileSheet::SubSheet const&ss, std::size_t idx) noexcept { uint8_t getPixel4Bpp(TileSheet::SubSheet const&ss, std::size_t idx) noexcept {
@ -355,12 +355,12 @@ ox::Result<SubSheetId> getIdFor(TileSheet const&ts, ox::StringViewCR path) noexc
/** /**
* Gets the offset in tiles of the desired subsheet. * Gets the offset in tiles of the desired subsheet.
*/ */
static ox::Result<uint32_t> getTileOffset( static ox::Result<unsigned> getTileOffset(
TileSheet::SubSheet const&ss, TileSheet::SubSheet const&ss,
ox::SpanView<ox::StringView> const&pNamePath, ox::SpanView<ox::StringView> const&pNamePath,
int8_t pBpp, int8_t pBpp,
std::size_t pIt = 0, std::size_t pIt = 0,
uint32_t pCurrentTotal = 0) noexcept { unsigned pCurrentTotal = 0) noexcept {
// pIt == pNamePath.size() - 1 && // pIt == pNamePath.size() - 1 &&
if (ss.name != pNamePath[pIt]) { if (ss.name != pNamePath[pIt]) {
return ox::Error(2, "Wrong branch"); return ox::Error(2, "Wrong branch");
@ -374,14 +374,12 @@ static ox::Result<uint32_t> getTileOffset(
if (!err) { if (!err) {
return offset; return offset;
} }
// Possible bug? Shoud this be usinga a recursive version of
// pixelCnt will count pixels in subsheets of sub as well.
pCurrentTotal += pixelCnt(sub, pBpp) / PixelsPerTile; pCurrentTotal += pixelCnt(sub, pBpp) / PixelsPerTile;
} }
return ox::Error(1, "SubSheet not found"); return ox::Error(1, "SubSheet not found");
} }
ox::Result<uint32_t> getTileOffset(TileSheet const&ts, ox::StringViewCR pNamePath) noexcept { ox::Result<unsigned> getTileOffset(TileSheet const&ts, ox::StringViewCR pNamePath) noexcept {
return core::getTileOffset(ts.subsheet, ox::split<8>(pNamePath, '.'), ts.bpp); return core::getTileOffset(ts.subsheet, ox::split<8>(pNamePath, '.'), ts.bpp);
} }

View File

@ -11,7 +11,7 @@ target_link_libraries(
target_compile_definitions( target_compile_definitions(
NostalgiaStudio PUBLIC NostalgiaStudio PUBLIC
OLYMPIC_APP_VERSION="dev build" OLYMPIC_APP_VERSION="d2024.12.2"
) )
install( install(

View File

@ -196,15 +196,15 @@ class AssetManager {
template<typename T> template<typename T>
class AssetTypeManager: public AssetTypeManagerBase { class AssetTypeManager: public AssetTypeManagerBase {
public: public:
using Loader = std::function<ox::Result<T>(ox::StringViewCR assetId)>; using Loader = std::function<ox::Result<T>(ox::StringView assetId)>;
private: private:
Loader m_loader{}; Loader m_loader{};
ox::HashMap<ox::String, ox::UPtr<AssetContainer<T>>> m_cache; ox::HashMap<ox::String, ox::UPtr<AssetContainer<T>>> m_cache;
public: public:
AssetTypeManager(Loader &&loader) noexcept: m_loader(std::move(loader)) {} AssetTypeManager(Loader loader) noexcept: m_loader(loader) {}
ox::Result<AssetRef<T>> getAsset(ox::StringViewCR assetId) const noexcept { ox::Result<AssetRef<T>> getAsset(ox::StringView const assetId) const noexcept {
OX_REQUIRE(out, m_cache.at(assetId)); OX_REQUIRE(out, m_cache.at(assetId));
if (!out || !*out) { if (!out || !*out) {
return ox::Error(1, "asset is null"); return ox::Error(1, "asset is null");
@ -212,7 +212,7 @@ class AssetManager {
return AssetRef<T>(out->get()); return AssetRef<T>(out->get());
} }
ox::Result<AssetRef<T>> loadAsset(ox::StringViewCR assetId) noexcept { ox::Result<AssetRef<T>> loadAsset(ox::StringView const assetId) noexcept {
auto &p = m_cache[assetId]; auto &p = m_cache[assetId];
OX_REQUIRE_M(obj, m_loader(assetId)); OX_REQUIRE_M(obj, m_loader(assetId));
if (!p) { if (!p) {
@ -224,7 +224,7 @@ class AssetManager {
return AssetRef<T>(p.get()); return AssetRef<T>(p.get());
} }
ox::Error reloadAsset(ox::StringViewCR assetId) noexcept { ox::Error reloadAsset(ox::StringView const assetId) noexcept {
auto &p = m_cache[assetId]; auto &p = m_cache[assetId];
OX_REQUIRE_M(obj, m_loader(assetId)); OX_REQUIRE_M(obj, m_loader(assetId));
if (!p) { if (!p) {
@ -247,7 +247,7 @@ class AssetManager {
}; };
ox::HashMap<ox::String, ox::UPtr<AssetTypeManagerBase>> m_assetTypeManagers; ox::HashMap<ox::String, ox::UPtr<AssetTypeManagerBase>> m_assetTypeManagers;
ox::HashMap<ox::String, ox::Signal<ox::Error(ox::StringViewCR assetId)>> m_fileUpdated; ox::HashMap<ox::String, ox::Signal<ox::Error(ox::StringView assetId)>> m_fileUpdated;
template<typename T> template<typename T>
ox::Result<AssetTypeManager<T>*> getTypeManager() noexcept { ox::Result<AssetTypeManager<T>*> getTypeManager() noexcept {
@ -273,18 +273,18 @@ class AssetManager {
} }
template<typename T> template<typename T>
ox::Result<AssetRef<T>> getAsset(ox::StringViewCR assetId) noexcept { ox::Result<AssetRef<T>> getAsset(ox::StringView assetId) noexcept {
OX_REQUIRE(m, getTypeManager<T>()); OX_REQUIRE(m, getTypeManager<T>());
return m->getAsset(assetId); return m->getAsset(assetId);
} }
ox::Error reloadAsset(ox::StringViewCR assetId) noexcept { ox::Error reloadAsset(ox::StringView assetId) noexcept {
m_fileUpdated[assetId].emit(assetId); m_fileUpdated[assetId].emit(assetId);
return {}; return {};
} }
template<typename T> template<typename T>
ox::Result<AssetRef<T>> loadAsset(ox::StringViewCR assetId) noexcept { ox::Result<AssetRef<T>> loadAsset(ox::StringView assetId) noexcept {
OX_REQUIRE(m, getTypeManager<T>()); OX_REQUIRE(m, getTypeManager<T>());
OX_REQUIRE(out, m->loadAsset(assetId)); OX_REQUIRE(out, m->loadAsset(assetId));
m_fileUpdated[assetId].connect(m, &AssetTypeManager<T>::reloadAsset); m_fileUpdated[assetId].connect(m, &AssetTypeManager<T>::reloadAsset);

View File

@ -12,7 +12,7 @@
namespace keel { namespace keel {
class Context; class Context;
using PackTransform = ox::Result<bool>(*)(Context&, ox::Buffer &clawData, ox::StringViewCR); using PackTransform = ox::Result<bool>(*)(Context&, ox::Buffer &clawData, ox::StringView);
class Context { class Context {
public: public:

View File

@ -70,7 +70,7 @@ constexpr auto makeLoader(Context &ctx) {
if (err != ox::Error_ClawTypeVersionMismatch && err != ox::Error_ClawTypeMismatch) { if (err != ox::Error_ClawTypeVersionMismatch && err != ox::Error_ClawTypeMismatch) {
return err; return err;
} }
OX_RETURN_ERROR(convert<T>(ctx, buff, obj)); OX_RETURN_ERROR(convert<T>(ctx, buff, &obj));
} }
return std::move(obj); return std::move(obj);
}; };

View File

@ -125,9 +125,7 @@ ox::Error preloadObj(
OX_RETURN_ERROR(err); OX_RETURN_ERROR(err);
keel::PreloadPtr const p{.preloadAddr = a}; keel::PreloadPtr const p{.preloadAddr = a};
OX_RETURN_ERROR(ox::writeMC(p).moveTo(buff)); OX_RETURN_ERROR(ox::writeMC(p).moveTo(buff));
auto const&pbufSz = pl.buff().size(); oxOutf("preloaded {} as a {} @ {} to {}\n", path, obj.type()->typeName, a, a + size);
oxOutf("preloaded {} as a {} @ {} to {} / {}, total size: {}\n",
path, obj.type()->typeName, a, a + size, pbufSz - 1, pbufSz - a);
} else { } else {
// strip the Claw header (it is not needed after preloading) and write back out to dest fs // strip the Claw header (it is not needed after preloading) and write back out to dest fs
OX_RETURN_ERROR(ox::writeMC(obj).moveTo(buff)); OX_RETURN_ERROR(ox::writeMC(obj).moveTo(buff));

View File

@ -16,50 +16,10 @@ namespace keel {
class Wrap { class Wrap {
public: public:
virtual ~Wrap() = default; virtual ~Wrap() = default;
[[nodiscard]]
virtual ox::CStringView typeName() const noexcept = 0;
[[nodiscard]]
virtual int typeVersion() const noexcept = 0;
[[nodiscard]]
virtual ox::UAnyPtr moveToCopy() noexcept = 0;
}; };
template<typename T> template<typename T>
class WrapT: public Wrap { class WrapInline: public Wrap {
public:
[[nodiscard]]
virtual constexpr T &obj() noexcept = 0;
ox::UAnyPtr moveToCopy() noexcept final {
return new T{std::move(obj())};
}
};
template<typename T>
class WrapRef final: public WrapT<T> {
private:
T &m_obj;
public:
constexpr explicit WrapRef(T &obj): m_obj{obj} {}
ox::CStringView typeName() const noexcept override {
return ox::ModelTypeName_v<T>;
}
int typeVersion() const noexcept override {
return ox::ModelTypeVersion_v<T>;
}
constexpr T &obj() noexcept override {
return m_obj;
}
};
template<typename T>
class WrapInline final: public WrapT<T> {
private: private:
T m_obj; T m_obj;
@ -70,15 +30,8 @@ class WrapInline final: public WrapT<T> {
constexpr explicit WrapInline(Args &&...args): m_obj(ox::forward<Args>(args)...) { constexpr explicit WrapInline(Args &&...args): m_obj(ox::forward<Args>(args)...) {
} }
ox::CStringView typeName() const noexcept override { [[nodiscard]]
return ox::ModelTypeName_v<T>; constexpr T &obj() noexcept {
}
int typeVersion() const noexcept override {
return ox::ModelTypeVersion_v<T>;
}
constexpr T &obj() noexcept override {
return m_obj; return m_obj;
} }
@ -91,7 +44,7 @@ constexpr ox::UPtr<Wrap> makeWrap(Args &&...args) noexcept {
template<typename T> template<typename T>
constexpr T &wrapCast(Wrap &ptr) noexcept { constexpr T &wrapCast(Wrap &ptr) noexcept {
return static_cast<WrapT<T>&>(ptr).obj(); return static_cast<WrapInline<T>&>(ptr).obj();
} }
class BaseConverter { class BaseConverter {
@ -117,10 +70,10 @@ class BaseConverter {
[[nodiscard]] [[nodiscard]]
constexpr bool matches( constexpr bool matches(
ox::StringViewCR srcTypeName, int const srcTypeVersion, ox::StringViewCR srcTypeName, int srcTypeVersion,
ox::StringViewCR dstTypeName, int const dstTypeVersion) const noexcept { ox::StringViewCR dstTypeName, int dstTypeVersion) const noexcept {
return srcMatches(srcTypeName, srcTypeVersion) return srcMatches(srcTypeName, srcTypeVersion)
&& dstMatches(dstTypeName, dstTypeVersion); && dstMatches(dstTypeName, dstTypeVersion);
} }
}; };
@ -156,17 +109,17 @@ class Converter: public BaseConverter {
ox::Result<ox::UPtr<Wrap>> convertPtrToPtr( ox::Result<ox::UPtr<Wrap>> convertPtrToPtr(
keel::Context &ctx, Wrap &src) const noexcept final { keel::Context &ctx, Wrap &src) const noexcept final {
ox::Result<ox::UPtr<Wrap>> dst{makeWrap<DstType>()}; auto dst = makeWrap<DstType>();
OX_RETURN_ERROR(convert(ctx, wrapCast<SrcType>(src), wrapCast<DstType>(*dst.value))); OX_RETURN_ERROR(convert(ctx, wrapCast<SrcType>(src), wrapCast<DstType>(*dst)));
return dst; return {std::move(dst)};
} }
ox::Result<ox::UPtr<Wrap>> convertBuffToPtr( ox::Result<ox::UPtr<Wrap>> convertBuffToPtr(
keel::Context &ctx, ox::BufferView const&srcBuff) const noexcept final { keel::Context &ctx, ox::BufferView const&srcBuff) const noexcept final {
OX_REQUIRE_M(src, readAsset<SrcType>(srcBuff)); OX_REQUIRE_M(src, readAsset<SrcType>(srcBuff));
ox::Result<ox::UPtr<Wrap>> dst{makeWrap<DstType>()}; auto dst = makeWrap<DstType>();
OX_RETURN_ERROR(convert(ctx, src, wrapCast<DstType>(*dst.value))); OX_RETURN_ERROR(convert(ctx, src, wrapCast<DstType>(*dst)));
return dst; return {std::move(dst)};
} }
protected: protected:
@ -180,68 +133,34 @@ ox::Result<ox::UPtr<Wrap>> convert(
ox::StringViewCR dstTypeName, ox::StringViewCR dstTypeName,
int dstTypeVersion) noexcept; int dstTypeVersion) noexcept;
ox::Result<ox::UPtr<Wrap>> convert(
keel::Context &ctx,
Wrap &src,
ox::StringViewCR dstTypeName,
int dstTypeVersion) noexcept;
ox::Result<ox::UPtr<Wrap>> convert(
keel::Context &ctx,
auto &src,
ox::StringViewCR dstTypeName,
int const dstTypeVersion) noexcept {
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> template<typename DstType>
ox::Result<DstType> convertObjToObj( ox::Result<DstType> convert(keel::Context &ctx, ox::BufferView const&srcBuffer) noexcept {
keel::Context &ctx, static constexpr auto DstTypeName = ox::requireModelTypeName<DstType>();
auto &src) noexcept { static constexpr auto DstTypeVersion = ox::requireModelTypeVersion<DstType>();
OX_REQUIRE_M(out, convert(ctx, WrapRef{src}, ox::ModelTypeName_v<DstType>, ox::ModelTypeVersion_v<DstType>)); OX_REQUIRE(out, convert(ctx, srcBuffer, DstTypeName, DstTypeVersion));
return std::move(wrapCast(*out));
}
template<typename DstType>
ox::Result<DstType> convert(keel::Context &ctx, ox::BufferView const&src) noexcept {
OX_REQUIRE(out, convert(ctx, src, ox::ModelTypeName_v<DstType>, ox::ModelTypeVersion_v<DstType>));
return std::move(wrapCast<DstType>(out)); return std::move(wrapCast<DstType>(out));
} }
template<typename DstType> template<typename DstType>
ox::Error convert(keel::Context &ctx, ox::BufferView const&buff, DstType &outObj) noexcept { ox::Error convert(keel::Context &ctx, ox::BufferView const&buff, DstType *outObj) noexcept {
OX_REQUIRE(out, convert(ctx, buff, ox::ModelTypeName_v<DstType>, ox::ModelTypeVersion_v<DstType>)); static constexpr auto DstTypeName = ox::requireModelTypeName<DstType>();
outObj = std::move(wrapCast<DstType>(*out)); static constexpr auto DstTypeVersion = ox::requireModelTypeVersion<DstType>();
return {}; OX_REQUIRE(outPtr, convert(ctx, buff, DstTypeName, DstTypeVersion));
} *outObj = std::move(wrapCast<DstType>(*outPtr));
template<typename DstType>
ox::Error convertObjToObj(keel::Context &ctx, auto &src, DstType &outObj) noexcept {
OX_REQUIRE(outPtr, convert(ctx, src, ox::ModelTypeName_v<DstType>, ox::ModelTypeVersion_v<DstType>));
outObj = std::move(wrapCast<DstType>(*outPtr));
return {}; return {};
} }
template<typename DstType> template<typename DstType>
ox::Result<ox::Buffer> convertBuffToBuff( ox::Result<ox::Buffer> convertBuffToBuff(
keel::Context &ctx, ox::BufferView const&src, ox::ClawFormat const fmt) noexcept { keel::Context &ctx, ox::BufferView const&srcBuffer, ox::ClawFormat fmt) noexcept {
OX_REQUIRE(out, convert(ctx, src, ox::ModelTypeName_v<DstType>, ox::ModelTypeVersion_v<DstType>)); static constexpr auto DstTypeName = ox::requireModelTypeName<DstType>();
static constexpr auto DstTypeVersion = ox::requireModelTypeVersion<DstType>();
OX_REQUIRE(out, convert(ctx, srcBuffer, DstTypeName, DstTypeVersion));
return ox::writeClaw<DstType>(wrapCast<DstType>(*out), fmt); return ox::writeClaw<DstType>(wrapCast<DstType>(*out), fmt);
} }
template<typename From, typename To, ox::ClawFormat fmt = ox::ClawFormat::Metal> template<typename From, typename To, ox::ClawFormat fmt = ox::ClawFormat::Metal>
ox::Result<bool> transformRule(keel::Context &ctx, ox::Buffer &buff, ox::StringViewCR typeId) noexcept { ox::Result<bool> transformRule(keel::Context &ctx, ox::Buffer &buff, ox::StringView typeId) noexcept {
if (typeId == ox::ModelTypeId_v<From>) { if (typeId == ox::ModelTypeId_v<From>) {
OX_RETURN_ERROR(keel::convertBuffToBuff<To>(ctx, buff, fmt).moveTo(buff)); OX_RETURN_ERROR(keel::convertBuffToBuff<To>(ctx, buff, fmt).moveTo(buff));
return true; return true;
@ -249,4 +168,5 @@ ox::Result<bool> transformRule(keel::Context &ctx, ox::Buffer &buff, ox::StringV
return false; return false;
}; };
} }

View File

@ -10,38 +10,30 @@ namespace keel {
static ox::Result<BaseConverter const*> findConverter( static ox::Result<BaseConverter const*> findConverter(
ox::SpanView<BaseConverter const*> const&converters, ox::SpanView<BaseConverter const*> const&converters,
ox::StringViewCR srcTypeName, ox::StringViewCR srcTypeName,
int const srcTypeVersion, int srcTypeVersion,
ox::StringViewCR dstTypeName, ox::StringViewCR dstTypeName,
int const dstTypeVersion) noexcept { int dstTypeVersion) noexcept {
for (auto const&c : converters) { for (auto const&c : converters) {
if (c->matches(srcTypeName, srcTypeVersion, dstTypeName, dstTypeVersion)) { if (c->matches(srcTypeName, srcTypeVersion, dstTypeName, dstTypeVersion)) {
return c; return c;
} }
} }
return ox::Error{1, "Could not find converter"}; return ox::Error(1, "Could not find converter");
}; };
static ox::Result<ox::UPtr<Wrap>> convert(BaseConverter const&c, Context &ctx, ox::BufferView const&src) noexcept {
return c.convertBuffToPtr(ctx, src);
}
static ox::Result<ox::UPtr<Wrap>> convert(BaseConverter const&c, Context &ctx, Wrap &src) noexcept {
return c.convertPtrToPtr(ctx, src);
}
static ox::Result<ox::UPtr<Wrap>> convert( static ox::Result<ox::UPtr<Wrap>> convert(
Context &ctx, keel::Context &ctx,
ox::SpanView<BaseConverter const*> const&converters, ox::SpanView<BaseConverter const*> const&converters,
auto &src, ox::BufferView const&srcBuffer,
ox::StringViewCR srcTypeName, ox::StringViewCR srcTypeName,
int const srcTypeVersion, int srcTypeVersion,
ox::StringViewCR dstTypeName, ox::StringViewCR dstTypeName,
int const dstTypeVersion) noexcept { int dstTypeVersion) noexcept {
// look for direct converter // look for direct converter
auto const [c, err] = findConverter( auto [c, err] = findConverter(
converters, srcTypeName, srcTypeVersion, dstTypeName, dstTypeVersion); converters, srcTypeName, srcTypeVersion, dstTypeName, dstTypeVersion);
if (!err) { if (!err) {
return convert(*c, ctx, src); return c->convertBuffToPtr(ctx, srcBuffer);
} }
// try to chain multiple converters // try to chain multiple converters
for (auto const&subConverter : converters) { for (auto const&subConverter : converters) {
@ -49,20 +41,20 @@ static ox::Result<ox::UPtr<Wrap>> convert(
continue; continue;
} }
const auto [intermediate, chainErr] = const auto [intermediate, chainErr] =
convert(ctx, converters, src, srcTypeName, srcTypeVersion, convert(ctx, converters, srcBuffer, srcTypeName, srcTypeVersion,
subConverter->srcTypeName(), subConverter->srcTypeVersion()); subConverter->srcTypeName(), subConverter->srcTypeVersion());
if (!chainErr) { if (!chainErr) {
return subConverter->convertPtrToPtr(ctx, *intermediate); return subConverter->convertPtrToPtr(ctx, *intermediate);
} }
} }
return ox::Error{1, "Could not convert between types"}; return ox::Error(1, "Could not convert between types");
} }
ox::Result<ox::UPtr<Wrap>> convert( ox::Result<ox::UPtr<Wrap>> convert(
Context &ctx, keel::Context &ctx,
ox::BufferView const&srcBuffer, ox::BufferView const&srcBuffer,
ox::StringViewCR dstTypeName, ox::StringViewCR dstTypeName,
int const dstTypeVersion) noexcept { int dstTypeVersion) noexcept {
OX_REQUIRE(hdr, readAssetHeader(srcBuffer)); OX_REQUIRE(hdr, readAssetHeader(srcBuffer));
return convert( return convert(
ctx, ctx,
@ -74,19 +66,4 @@ ox::Result<ox::UPtr<Wrap>> convert(
dstTypeVersion); dstTypeVersion);
} }
ox::Result<ox::UPtr<Wrap>> convert(
Context &ctx,
Wrap &src,
ox::StringViewCR dstTypeName,
int const dstTypeVersion) noexcept {
return convert(
ctx,
converters(ctx),
src,
src.typeName(),
src.typeVersion(),
dstTypeName,
dstTypeVersion);
}
} }

View File

@ -2,7 +2,6 @@ add_library(
StudioAppLib StudioAppLib
aboutpopup.cpp aboutpopup.cpp
clawviewer.cpp clawviewer.cpp
deleteconfirmation.cpp
filedialogmanager.cpp filedialogmanager.cpp
main.cpp main.cpp
newmenu.cpp newmenu.cpp

View File

@ -1,56 +0,0 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include <studio/imguiutil.hpp>
#include "deleteconfirmation.hpp"
namespace studio {
DeleteConfirmation::DeleteConfirmation(StudioContext &ctx) noexcept:
m_ctx{ctx} {
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) != ig::PopupResponse::None) {
deleteFile.emit(m_path);
close();
}
});
break;
}
}
}

View File

@ -1,44 +0,0 @@
/*
* 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;
StudioContext &m_ctx;
public:
ox::Signal<ox::Error(ox::StringViewCR path)> deleteFile;
DeleteConfirmation(StudioContext &ctx) 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;
};
}

View File

@ -14,7 +14,7 @@ namespace studio {
NewMenu::NewMenu() noexcept { NewMenu::NewMenu() noexcept {
setTitle("New Item"); setTitle("New Item");
setSize({280, 180}); setSize({230, 140});
} }
void NewMenu::open() noexcept { void NewMenu::open() noexcept {
@ -22,12 +22,6 @@ void NewMenu::open() noexcept {
m_selectedType = 0; m_selectedType = 0;
m_itemName = ""; m_itemName = "";
m_typeName = ""; m_typeName = "";
m_path = "";
}
void NewMenu::openPath(ox::StringParam path) noexcept {
open();
m_path = std::move(path);
} }
void NewMenu::close() noexcept { void NewMenu::close() noexcept {
@ -39,7 +33,7 @@ bool NewMenu::isOpen() const noexcept {
return m_open; return m_open;
} }
void NewMenu::draw(StudioContext &sctx) noexcept { void NewMenu::draw(studio::StudioContext &sctx) noexcept {
switch (m_stage) { switch (m_stage) {
case Stage::Opening: case Stage::Opening:
ImGui::OpenPopup(title().c_str()); ImGui::OpenPopup(title().c_str());
@ -52,87 +46,51 @@ void NewMenu::draw(StudioContext &sctx) noexcept {
case Stage::NewItemName: case Stage::NewItemName:
drawNewItemName(sctx); drawNewItemName(sctx);
break; break;
case Stage::NewItemTemplate:
drawNewItemTemplate(sctx);
break;
case Stage::Closed: case Stage::Closed:
m_open = false; m_open = false;
break; break;
} }
} }
void NewMenu::addItemMaker(ox::UPtr<ItemMaker> &&im) noexcept { void NewMenu::addItemMaker(ox::UniquePtr<studio::ItemMaker> &&im) noexcept {
m_types.emplace_back(std::move(im)); m_types.emplace_back(std::move(im));
std::sort( std::sort(
m_types.begin(), m_types.end(), m_types.begin(), m_types.end(),
[](ox::UPtr<ItemMaker> const&im1, ox::UPtr<ItemMaker> const&im2) { [](ox::UPtr<ItemMaker> const&im1, ox::UPtr<ItemMaker> const&im2) {
return im1->typeDisplayName() < im2->typeDisplayName(); return im1->typeName < im2->typeName;
}); });
} }
void NewMenu::installItemTemplate(ox::UPtr<ItemTemplate> &tmplt) noexcept { void NewMenu::drawNewItemType(studio::StudioContext &sctx) noexcept {
for (auto const&im : m_types) { drawWindow(sctx.tctx, &m_open, [this] {
if (im->installTemplate(tmplt)) { auto const allocSz = m_types.size() * sizeof(char const*);
break; 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;
} }
} 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::drawNewItemTemplate(StudioContext &sctx) noexcept { void NewMenu::drawNewItemName(studio::StudioContext &sctx) noexcept {
drawWindow(sctx.tctx, m_open, [this] { drawWindow(sctx.tctx, &m_open, [this, &sctx] {
auto const&templates = auto const typeIdx = static_cast<std::size_t>(m_selectedType);
m_types[m_selectedType]->itemTemplates(); if (typeIdx < m_types.size()) {
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); ig::InputText("Name", m_itemName);
} }
drawLastPageButtons(sctx); drawLastPageButtons(sctx);
}); });
} }
void NewMenu::drawButtons(Stage const prev, Stage const next) noexcept { void NewMenu::drawFirstPageButtons() 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 = prev;
}
ImGui::SameLine();
if (ImGui::Button("Next", btnSz)) {
m_stage = next;
}
ImGui::SameLine();
if (ImGui::Button("Cancel", btnSz)) {
ImGui::CloseCurrentPopup();
m_stage = Stage::Closed;
}
}
void NewMenu::drawFirstPageButtons(Stage const next) noexcept {
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 130); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 130);
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - 20); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - 20);
constexpr ImVec2 btnSz{60, 20}; auto const btnSz = ImVec2(60, 20);
if (ImGui::Button("Next", btnSz)) { if (ImGui::Button("Next", btnSz)) {
m_stage = next; m_stage = Stage::NewItemName;
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Cancel", btnSz)) { if (ImGui::Button("Cancel", btnSz)) {
@ -141,37 +99,35 @@ void NewMenu::drawFirstPageButtons(Stage const next) noexcept {
} }
} }
void NewMenu::drawLastPageButtons(StudioContext &sctx) noexcept { void NewMenu::drawLastPageButtons(studio::StudioContext &sctx) noexcept {
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 198); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 138);
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - 20); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - 20);
constexpr ImVec2 btnSz{60, 20}; if (ImGui::Button("Back")) {
if (ImGui::Button("Back", btnSz)) {
m_stage = Stage::NewItemType; m_stage = Stage::NewItemType;
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Finish", btnSz)) { if (ImGui::Button("Finish")) {
finish(sctx); finish(sctx);
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Quit", btnSz)) { if (ImGui::Button("Quit")) {
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
m_stage = Stage::Closed; m_stage = Stage::Closed;
} }
} }
void NewMenu::finish(StudioContext &sctx) noexcept { void NewMenu::finish(studio::StudioContext &sctx) noexcept {
if (m_itemName.len() == 0) { 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; return;
} }
auto const&im = *m_types[m_selectedType]; auto const&typeMaker = *m_types[static_cast<std::size_t>(m_selectedType)];
auto const path = m_path.len() ? if (sctx.project->exists(typeMaker.itemPath(m_itemName))) {
im.itemPath(m_itemName, m_path) : im.itemPath(m_itemName); oxLogError(ox::Error(1, "New file error: file already exists"));
if (sctx.project->exists(path)) {
oxLogError(ox::Error{1, "New file error: file already exists"});
return; return;
} }
if (auto const err = im.write(sctx, path, m_selectedTemplate)) { auto const [path, err] = typeMaker.write(sctx, m_itemName);
if (err) {
oxLogError(err); oxLogError(err);
return; return;
} }

View File

@ -13,14 +13,13 @@
namespace studio { namespace studio {
class NewMenu final: public Popup { class NewMenu: public studio::Popup {
public: public:
enum class Stage { enum class Stage {
Closed, Closed,
Opening, Opening,
NewItemType, NewItemType,
NewItemName, NewItemName,
NewItemTemplate,
}; };
// emits path parameter // emits path parameter
@ -30,17 +29,13 @@ class NewMenu final: public Popup {
Stage m_stage = Stage::Closed; Stage m_stage = Stage::Closed;
ox::String m_typeName; ox::String m_typeName;
ox::IString<255> m_itemName; ox::IString<255> m_itemName;
ox::String m_path; ox::Vector<ox::UniquePtr<studio::ItemMaker>> m_types;
ox::Vector<ox::UPtr<studio::ItemMaker>> m_types; int m_selectedType = 0;
size_t m_selectedType = 0;
size_t m_selectedTemplate = 0;
bool m_open = false; bool m_open = false;
public: public:
NewMenu() noexcept; NewMenu() noexcept;
void openPath(ox::StringParam path) noexcept;
void open() noexcept override; void open() noexcept override;
void close() noexcept override; void close() noexcept override;
@ -48,70 +43,37 @@ class NewMenu final: public Popup {
[[nodiscard]] [[nodiscard]]
bool isOpen() const noexcept override; bool isOpen() const noexcept override;
void draw(StudioContext &sctx) noexcept override; void draw(studio::StudioContext &sctx) noexcept override;
template<typename T> template<typename T>
void addItemType( void addItemType(ox::String name, ox::String parentDir, ox::String fileExt, T itemTempl, ox::ClawFormat pFmt = ox::ClawFormat::Metal) noexcept;
ox::StringParam displayName,
ox::StringParam parentDir,
ox::StringParam fileExt,
T itemTempl,
ox::ClawFormat pFmt = ox::ClawFormat::Metal) noexcept;
template<typename T> template<typename T>
void addItemType( void addItemType(ox::String name, ox::String parentDir, ox::String fileExt, ox::ClawFormat pFmt = ox::ClawFormat::Metal) noexcept;
ox::StringParam displayName,
ox::StringParam parentDir,
ox::StringParam fileExt,
ox::ClawFormat pFmt = ox::ClawFormat::Metal) noexcept;
void addItemMaker(ox::UPtr<ItemMaker> &&im) noexcept; void addItemMaker(ox::UniquePtr<studio::ItemMaker> &&im) noexcept;
void installItemTemplate(ox::UPtr<ItemTemplate> &tmplt) noexcept;
private: private:
void drawNewItemType(StudioContext const&sctx) noexcept; void drawNewItemType(studio::StudioContext &sctx) noexcept;
void drawNewItemName(StudioContext &sctx) noexcept; void drawNewItemName(studio::StudioContext &sctx) noexcept;
void drawNewItemTemplate(StudioContext &sctx) noexcept; void drawFirstPageButtons() noexcept;
void drawButtons(Stage prev, Stage next) noexcept; void drawLastPageButtons(studio::StudioContext &sctx) noexcept;
void drawFirstPageButtons(Stage next) noexcept; void finish(studio::StudioContext &sctx) noexcept;
void drawLastPageButtons(StudioContext &sctx) noexcept;
void finish(StudioContext &sctx) noexcept;
}; };
template<typename T> template<typename T>
void NewMenu::addItemType( void NewMenu::addItemType(ox::String displayName, ox::String parentDir, ox::String fileExt, T itemTempl, ox::ClawFormat pFmt) noexcept {
ox::StringParam displayName, m_types.emplace_back(ox::make<studio::ItemMakerT<T>>(std::move(displayName), std::move(parentDir), std::move(fileExt), std::move(itemTempl), pFmt));
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> template<typename T>
void NewMenu::addItemType( void NewMenu::addItemType(ox::String displayName, ox::String parentDir, ox::String fileExt, ox::ClawFormat pFmt) noexcept {
ox::StringParam displayName, m_types.emplace_back(ox::make<studio::ItemMakerT<T>>(std::move(displayName), std::move(parentDir), std::move(fileExt), pFmt));
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));
} }
} }

View File

@ -49,7 +49,7 @@ void NewProject::draw(studio::StudioContext &ctx) noexcept {
} }
void NewProject::drawNewProjectName(studio::StudioContext &sctx) 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); ig::InputText("Name", m_projectName);
ImGui::Text("Path: %s", m_projectPath.c_str()); ImGui::Text("Path: %s", m_projectPath.c_str());
if (ImGui::Button("Browse")) { if (ImGui::Button("Browse")) {

View File

@ -12,12 +12,12 @@ namespace studio {
static ox::Result<ox::UniquePtr<ProjectTreeModel>> buildProjectTreeModel( static ox::Result<ox::UniquePtr<ProjectTreeModel>> buildProjectTreeModel(
ProjectExplorer &explorer, ProjectExplorer &explorer,
ox::StringParam name, ox::StringView name,
ox::StringView path, ox::StringView path,
ProjectTreeModel *parent) noexcept { ProjectTreeModel *parent) noexcept {
auto const fs = explorer.romFs(); auto const fs = explorer.romFs();
OX_REQUIRE(stat, fs->stat(path)); OX_REQUIRE(stat, fs->stat(path));
auto out = ox::make_unique<ProjectTreeModel>(explorer, std::move(name), parent); auto out = ox::make_unique<ProjectTreeModel>(explorer, ox::String(name), parent);
if (stat.fileType == ox::FileType::Directory) { if (stat.fileType == ox::FileType::Directory) {
OX_REQUIRE_M(children, fs->ls(path)); OX_REQUIRE_M(children, fs->ls(path));
std::sort(children.begin(), children.end()); 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) { ProjectExplorer::ProjectExplorer(turbine::Context &ctx) noexcept: m_ctx(ctx) {
} }
void ProjectExplorer::draw(StudioContext &ctx) noexcept { void ProjectExplorer::draw(studio::StudioContext &ctx) noexcept {
auto const viewport = ImGui::GetContentRegionAvail(); auto const viewport = ImGui::GetContentRegionAvail();
ImGui::BeginChild("ProjectExplorer", ImVec2(300, viewport.y), true); ImGui::BeginChild("ProjectExplorer", ImVec2(300, viewport.y), true);
ImGui::SetNextItemOpen(true); ImGui::SetNextItemOpen(true);
@ -54,7 +54,7 @@ void ProjectExplorer::setModel(ox::UPtr<ProjectTreeModel> &&model) noexcept {
ox::Error ProjectExplorer::refreshProjectTreeModel(ox::StringViewCR) noexcept { ox::Error ProjectExplorer::refreshProjectTreeModel(ox::StringViewCR) noexcept {
OX_REQUIRE_M(model, buildProjectTreeModel(*this, "Project", "/", nullptr)); OX_REQUIRE_M(model, buildProjectTreeModel(*this, "Project", "/", nullptr));
setModel(std::move(model)); setModel(std::move(model));
return {}; return ox::Error(0);
} }
} }

View File

@ -12,31 +12,27 @@
namespace studio { namespace studio {
class ProjectExplorer: public Widget { class ProjectExplorer: public studio::Widget {
private: private:
ox::UPtr<ProjectTreeModel> m_treeModel; ox::UPtr<ProjectTreeModel> m_treeModel;
turbine::Context &m_ctx; turbine::Context &m_ctx;
public: 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; explicit ProjectExplorer(turbine::Context &ctx) noexcept;
void draw(StudioContext &ctx) noexcept override; void draw(studio::StudioContext &ctx) noexcept override;
void setModel(ox::UPtr<ProjectTreeModel> &&model) noexcept; void setModel(ox::UPtr<ProjectTreeModel> &&model) noexcept;
ox::Error refreshProjectTreeModel(ox::StringViewCR = {}) noexcept; ox::Error refreshProjectTreeModel(ox::StringViewCR = {}) noexcept;
[[nodiscard]] [[nodiscard]]
ox::FileSystem *romFs() noexcept { inline ox::FileSystem *romFs() noexcept {
return rom(m_ctx); return rom(m_ctx);
} }
// slots
public:
ox::Signal<ox::Error(ox::StringView const&)> fileChosen;
}; };
} }

View File

@ -4,17 +4,13 @@
#include <imgui.h> #include <imgui.h>
#include <studio/imguiutil.hpp>
#include "projectexplorer.hpp" #include "projectexplorer.hpp"
#include "projecttreemodel.hpp" #include "projecttreemodel.hpp"
namespace studio { namespace studio {
ProjectTreeModel::ProjectTreeModel( ProjectTreeModel::ProjectTreeModel(ProjectExplorer &explorer, ox::String name,
ProjectExplorer &explorer, ProjectTreeModel *parent) noexcept:
ox::StringParam name,
ProjectTreeModel *parent) noexcept:
m_explorer(explorer), m_explorer(explorer),
m_parent(parent), m_parent(parent),
m_name(std::move(name)) { m_name(std::move(name)) {
@ -31,14 +27,10 @@ void ProjectTreeModel::draw(turbine::Context &ctx) const noexcept {
constexpr auto dirFlags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; constexpr auto dirFlags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
if (!m_children.empty()) { if (!m_children.empty()) {
if (ImGui::TreeNodeEx(m_name.c_str(), dirFlags)) { if (ImGui::TreeNodeEx(m_name.c_str(), dirFlags)) {
drawDirContextMenu();
for (auto const&child : m_children) { for (auto const&child : m_children) {
child->draw(ctx); child->draw(ctx);
} }
ImGui::TreePop(); ImGui::TreePop();
} else {
ig::IDStackItem const idStackItem{m_name};
drawDirContextMenu();
} }
} else { } else {
auto const path = fullPath(); auto const path = fullPath();
@ -47,12 +39,6 @@ void ProjectTreeModel::draw(turbine::Context &ctx) const noexcept {
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0)) { if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0)) {
m_explorer.fileChosen.emit(path); m_explorer.fileChosen.emit(path);
} }
if (ImGui::BeginPopupContextItem("FileMenu", ImGuiPopupFlags_MouseButtonRight)) {
if (ImGui::MenuItem("Delete")) {
m_explorer.deleteItem.emit(path);
}
ImGui::EndPopup();
}
ImGui::TreePop(); ImGui::TreePop();
} }
} }
@ -62,18 +48,6 @@ void ProjectTreeModel::setChildren(ox::Vector<ox::UPtr<ProjectTreeModel>> childr
m_children = std::move(children); m_children = std::move(children);
} }
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());
}
ImGui::EndPopup();
}
}
ox::BasicString<255> ProjectTreeModel::fullPath() const noexcept { ox::BasicString<255> ProjectTreeModel::fullPath() const noexcept {
if (m_parent) { if (m_parent) {
return m_parent->fullPath() + "/" + ox::StringView(m_name); return m_parent->fullPath() + "/" + ox::StringView(m_name);

View File

@ -17,11 +17,9 @@ class ProjectTreeModel {
ProjectTreeModel *m_parent = nullptr; ProjectTreeModel *m_parent = nullptr;
ox::String m_name; ox::String m_name;
ox::Vector<ox::UPtr<ProjectTreeModel>> m_children; ox::Vector<ox::UPtr<ProjectTreeModel>> m_children;
public: public:
explicit ProjectTreeModel( explicit ProjectTreeModel(class ProjectExplorer &explorer, ox::String name,
ProjectExplorer &explorer, ox::StringParam name, ProjectTreeModel *parent = nullptr) noexcept;
ProjectTreeModel *parent = nullptr) noexcept;
ProjectTreeModel(ProjectTreeModel &&other) noexcept; ProjectTreeModel(ProjectTreeModel &&other) noexcept;
@ -30,11 +28,8 @@ class ProjectTreeModel {
void setChildren(ox::Vector<ox::UPtr<ProjectTreeModel>> children) noexcept; void setChildren(ox::Vector<ox::UPtr<ProjectTreeModel>> children) noexcept;
private: private:
void drawDirContextMenu() const noexcept;
[[nodiscard]] [[nodiscard]]
ox::BasicString<255> fullPath() const noexcept; ox::BasicString<255> fullPath() const noexcept;
}; };
} }

View File

@ -18,9 +18,9 @@
namespace studio { namespace studio {
static ox::Vector<Module const*> modules; static ox::Vector<studio::Module const*> modules;
void registerModule(Module const*mod) noexcept { void registerModule(studio::Module const*mod) noexcept {
if (mod) { if (mod) {
modules.emplace_back(mod); modules.emplace_back(mod);
} }
@ -45,27 +45,25 @@ OX_MODEL_END()
StudioUI::StudioUI(turbine::Context &ctx, ox::StringParam projectDataDir) noexcept: StudioUI::StudioUI(turbine::Context &ctx, ox::StringParam projectDataDir) noexcept:
m_sctx(*this, ctx), m_sctx(*this, ctx),
m_tctx(ctx), m_ctx(ctx),
m_projectDataDir(std::move(projectDataDir)), m_projectDataDir(std::move(projectDataDir)),
m_projectExplorer(m_tctx), m_projectExplorer(m_ctx),
m_newProject(m_projectDataDir), m_newProject(m_projectDataDir),
m_aboutPopup(m_tctx) { m_aboutPopup(m_ctx) {
turbine::setApplicationData(m_tctx, &m_sctx); turbine::setApplicationData(m_ctx, &m_sctx);
m_projectExplorer.fileChosen.connect(this, &StudioUI::openFile); m_projectExplorer.fileChosen.connect(this, &StudioUI::openFile);
m_projectExplorer.addItem.connect(this, &StudioUI::addFile);
m_projectExplorer.deleteItem.connect(this, &StudioUI::deleteFile);
m_newProject.finished.connect(this, &StudioUI::createOpenProject); m_newProject.finished.connect(this, &StudioUI::createOpenProject);
m_newMenu.finished.connect(this, &StudioUI::openFile); m_newMenu.finished.connect(this, &StudioUI::openFile);
ImGui::GetIO().IniFilename = nullptr; ImGui::GetIO().IniFilename = nullptr;
loadModules(); loadModules();
// open project and files // open project and files
auto const [config, err] = studio::readConfig<StudioConfig>(keelCtx(m_tctx)); auto const [config, err] = studio::readConfig<StudioConfig>(keelCtx(m_ctx));
m_showProjectExplorer = config.showProjectExplorer; m_showProjectExplorer = config.showProjectExplorer;
if (!err) { if (!err) {
auto const openProjErr = openProjectPath(config.projectPath); auto const openProjErr = openProjectPath(config.projectPath);
if (!openProjErr) { if (!openProjErr) {
for (auto const&f: config.openFiles) { for (auto const&f: config.openFiles) {
auto const openFileErr = openFileActiveTab(f, config.activeTabItemName == f); auto openFileErr = openFileActiveTab(f, config.activeTabItemName == f);
if (openFileErr) { if (openFileErr) {
oxErrorf("\nCould not open editor for file:\n\t{}\nReason:\n\t{}\n", f, toStr(openFileErr)); oxErrorf("\nCould not open editor for file:\n\t{}\nReason:\n\t{}\n", f, toStr(openFileErr));
continue; continue;
@ -79,13 +77,13 @@ StudioUI::StudioUI(turbine::Context &ctx, ox::StringParam projectDataDir) noexce
} else { } else {
oxErrf( oxErrf(
"Could not open studio config file: {}: {} ({}:{})\n", "Could not open studio config file: {}: {} ({}:{})\n",
err.errCode, toStr(err), err.src.file_name(), err.src.line()); err.errCode, toStr(err), err.file, err.line);
} }
} }
} }
void StudioUI::handleKeyEvent(turbine::Key const key, bool const down) noexcept { void StudioUI::handleKeyEvent(turbine::Key key, bool down) noexcept {
for (auto const p : m_popups) { for (auto p : m_popups) {
if (p->isOpen()) { if (p->isOpen()) {
if (key == turbine::Key::Escape) { if (key == turbine::Key::Escape) {
p->close(); p->close();
@ -120,13 +118,13 @@ void StudioUI::draw() noexcept {
for (auto &w: m_widgets) { for (auto &w: m_widgets) {
w->draw(m_sctx); w->draw(m_sctx);
} }
for (auto const p: m_popups) { for (auto p: m_popups) {
p->draw(m_sctx); p->draw(m_sctx);
} }
} }
ImGui::End(); ImGui::End();
handleKeyInput(); handleKeyInput();
m_taskRunner.update(m_tctx); m_taskRunner.update(m_ctx);
} }
void StudioUI::drawMenu() noexcept { void StudioUI::drawMenu() noexcept {
@ -145,7 +143,7 @@ void StudioUI::drawMenu() noexcept {
m_activeEditor->save(); m_activeEditor->save();
} }
if (ImGui::MenuItem("Quit", "Ctrl+Q")) { if (ImGui::MenuItem("Quit", "Ctrl+Q")) {
turbine::requestShutdown(m_tctx); turbine::requestShutdown(m_ctx);
} }
ImGui::EndMenu(); ImGui::EndMenu();
} }
@ -170,7 +168,7 @@ void StudioUI::drawMenu() noexcept {
ImGui::EndMenu(); ImGui::EndMenu();
} }
if (ImGui::BeginMenu("View")) { if (ImGui::BeginMenu("View")) {
if (ImGui::MenuItem("Project Explorer", "Ctrl+Shift+1", m_showProjectExplorer)) { if (ImGui::MenuItem("Project Explorer", "Ctrl+1", m_showProjectExplorer)) {
toggleProjectExplorer(); toggleProjectExplorer();
} }
ImGui::EndMenu(); ImGui::EndMenu();
@ -206,10 +204,9 @@ void StudioUI::drawTabs() noexcept {
if (ImGui::BeginTabItem(e->itemDisplayName().c_str(), &open, flags)) { if (ImGui::BeginTabItem(e->itemDisplayName().c_str(), &open, flags)) {
if (m_activeEditor != e.get()) [[unlikely]] { if (m_activeEditor != e.get()) [[unlikely]] {
m_activeEditor = e.get(); m_activeEditor = e.get();
studio::editConfig<StudioConfig>(keelCtx(m_tctx), [&](StudioConfig &config) { studio::editConfig<StudioConfig>(keelCtx(m_ctx), [&](StudioConfig &config) {
config.activeTabItemName = m_activeEditor->itemPath(); config.activeTabItemName = m_activeEditor->itemPath();
}); });
turbine::setRefreshWithin(m_tctx, 0);
} else [[likely]] { } else [[likely]] {
if (m_activeEditorUpdatePending == e.get()) [[unlikely]] { if (m_activeEditorUpdatePending == e.get()) [[unlikely]] {
m_activeEditorUpdatePending = nullptr; m_activeEditorUpdatePending = nullptr;
@ -230,7 +227,7 @@ void StudioUI::drawTabs() noexcept {
try { try {
OX_THROW_ERROR(m_editors.erase(it).moveTo(it)); OX_THROW_ERROR(m_editors.erase(it).moveTo(it));
} catch (ox::Exception const&ex) { } catch (ox::Exception const&ex) {
oxErrf("Editor tab deletion failed: {} ({}:{})\n", ex.what(), ex.src.file_name(), ex.src.line()); oxErrf("Editor tab deletion failed: {} ({}:{})\n", ex.what(), ex.file, ex.line);
} catch (std::exception const&ex) { } catch (std::exception const&ex) {
oxErrf("Editor tab deletion failed: {}\n", ex.what()); oxErrf("Editor tab deletion failed: {}\n", ex.what());
} }
@ -240,23 +237,19 @@ void StudioUI::drawTabs() noexcept {
} }
} }
void StudioUI::loadEditorMaker(EditorMaker const&editorMaker) noexcept { void StudioUI::loadEditorMaker(studio::EditorMaker const&editorMaker) noexcept {
for (auto const&ext : editorMaker.fileTypes) { for (auto const&ext : editorMaker.fileTypes) {
m_editorMakers[ext] = editorMaker.make; m_editorMakers[ext] = editorMaker.make;
} }
} }
void StudioUI::loadModule(Module const&mod) noexcept { void StudioUI::loadModule(studio::Module const&mod) noexcept {
for (auto const&editorMaker : mod.editors(m_sctx)) { for (auto const&editorMaker : mod.editors(m_sctx)) {
loadEditorMaker(editorMaker); loadEditorMaker(editorMaker);
} }
for (auto &im : mod.itemMakers(m_sctx)) { for (auto &im : mod.itemMakers(m_sctx)) {
m_newMenu.addItemMaker(std::move(im)); m_newMenu.addItemMaker(std::move(im));
} }
auto tmplts = mod.itemTemplates(m_sctx);
for (auto &t : tmplts) {
m_newMenu.installItemTemplate(t);
}
} }
void StudioUI::loadModules() noexcept { void StudioUI::loadModules() noexcept {
@ -267,20 +260,20 @@ void StudioUI::loadModules() noexcept {
void StudioUI::toggleProjectExplorer() noexcept { void StudioUI::toggleProjectExplorer() noexcept {
m_showProjectExplorer = !m_showProjectExplorer; m_showProjectExplorer = !m_showProjectExplorer;
editConfig<StudioConfig>(keelCtx(m_tctx), [&](StudioConfig &config) { studio::editConfig<StudioConfig>(keelCtx(m_ctx), [&](StudioConfig &config) {
config.showProjectExplorer = m_showProjectExplorer; config.showProjectExplorer = m_showProjectExplorer;
}); });
} }
void StudioUI::redo() noexcept { void StudioUI::redo() noexcept {
auto const undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr; auto undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr;
if (undoStack && undoStack->canRedo()) { if (undoStack && undoStack->canRedo()) {
oxLogError(m_activeEditor->undoStack()->redo()); oxLogError(m_activeEditor->undoStack()->redo());
} }
} }
void StudioUI::undo() noexcept { void StudioUI::undo() noexcept {
auto const undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr; auto undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr;
if (undoStack && undoStack->canUndo()) { if (undoStack && undoStack->canUndo()) {
oxLogError(m_activeEditor->undoStack()->undo()); oxLogError(m_activeEditor->undoStack()->undo());
} }
@ -299,7 +292,7 @@ void StudioUI::handleKeyInput() noexcept {
m_activeEditor->copy(); m_activeEditor->copy();
} }
} else if (ImGui::IsKeyPressed(ImGuiKey_N)) { } else if (ImGui::IsKeyPressed(ImGuiKey_N)) {
if (turbine::buttonDown(m_tctx, turbine::Key::Mod_Shift)) { if (turbine::buttonDown(m_ctx, turbine::Key::Mod_Shift)) {
m_newProject.open(); m_newProject.open();
} else { } else {
m_newMenu.open(); m_newMenu.open();
@ -309,7 +302,7 @@ void StudioUI::handleKeyInput() noexcept {
} else if (ImGui::IsKeyPressed(ImGuiKey_S)) { } else if (ImGui::IsKeyPressed(ImGuiKey_S)) {
save(); save();
} else if (ImGui::IsKeyPressed(ImGuiKey_Q)) { } else if (ImGui::IsKeyPressed(ImGuiKey_Q)) {
turbine::requestShutdown(m_tctx); turbine::requestShutdown(m_ctx);
} else if (ImGui::IsKeyPressed(ImGuiKey_V)) { } else if (ImGui::IsKeyPressed(ImGuiKey_V)) {
if (m_activeEditor && m_activeEditor->pasteEnabled()) { if (m_activeEditor && m_activeEditor->pasteEnabled()) {
m_activeEditor->paste(); m_activeEditor->paste();
@ -324,36 +317,12 @@ void StudioUI::handleKeyInput() noexcept {
} else if (ImGui::IsKeyPressed(ImGuiKey_Z)) { } else if (ImGui::IsKeyPressed(ImGuiKey_Z)) {
auto const undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr; auto const undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr;
if (undoStack) { oxLogError(undoStack->undo()); } if (undoStack) { oxLogError(undoStack->undo()); }
} else if (ImGui::IsKeyPressed(ImGuiKey_1) && ImGui::IsKeyDown(ImGuiKey_ModShift)) { } else if (ImGui::IsKeyPressed(ImGuiKey_1)) {
toggleProjectExplorer(); toggleProjectExplorer();
} else {
if (!m_editors.empty()) {
auto const range = ox::min<size_t>(9u, m_editors.size()) - 1;
for (auto i = 0u; i < 9; ++i) {
if (ImGui::IsKeyPressed(static_cast<ImGuiKey>(static_cast<int>(ImGuiKey_1) + i))) {
m_activeEditor = m_editors[i < m_editors.size() ? i : range].get();
m_activeEditorUpdatePending = m_activeEditor;
}
}
if (ImGui::IsKeyPressed(ImGuiKey_0)) {
m_activeEditor = m_editors[10 < m_editors.size() ? 10 : range].get();
m_activeEditorUpdatePending = m_activeEditor;
}
}
} }
} }
} }
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 { ox::Error StudioUI::createOpenProject(ox::StringViewCR path) noexcept {
std::error_code ec; std::error_code ec;
std::filesystem::create_directories(toStdStringView(path), ec); std::filesystem::create_directories(toStdStringView(path), ec);
@ -364,18 +333,18 @@ ox::Error StudioUI::createOpenProject(ox::StringViewCR path) noexcept {
ox::Error StudioUI::openProjectPath(ox::StringParam path) noexcept { ox::Error StudioUI::openProjectPath(ox::StringParam path) noexcept {
OX_REQUIRE_M(fs, keel::loadRomFs(path.view())); OX_REQUIRE_M(fs, keel::loadRomFs(path.view()));
OX_RETURN_ERROR(keel::setRomFs(keelCtx(m_tctx), std::move(fs))); OX_RETURN_ERROR(keel::setRomFs(keelCtx(m_ctx), std::move(fs)));
OX_RETURN_ERROR( OX_RETURN_ERROR(
ox::make_unique_catch<Project>(keelCtx(m_tctx), std::move(path), m_projectDataDir) ox::make_unique_catch<studio::Project>(keelCtx(m_ctx), std::move(path), m_projectDataDir)
.moveTo(m_project)); .moveTo(m_project));
m_sctx.project = m_project.get(); auto const sctx = applicationData<studio::StudioContext>(m_ctx);
m_deleteConfirmation.deleteFile.connect(m_sctx.project, &Project::deleteItem); sctx->project = m_project.get();
turbine::setWindowTitle(m_tctx, ox::sfmt("{} - {}", keelCtx(m_tctx).appName, m_project->projectPath())); turbine::setWindowTitle(m_ctx, ox::sfmt("{} - {}", keelCtx(m_ctx).appName, m_project->projectPath()));
m_project->fileAdded.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel); m_project->fileAdded.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel);
m_project->fileDeleted.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel); m_project->fileDeleted.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel);
m_openFiles.clear(); m_openFiles.clear();
m_editors.clear(); m_editors.clear();
studio::editConfig<StudioConfig>(keelCtx(m_tctx), [&](StudioConfig &config) { studio::editConfig<StudioConfig>(keelCtx(m_ctx), [&](StudioConfig &config) {
config.projectPath = ox::String(m_project->projectPath()); config.projectPath = ox::String(m_project->projectPath());
config.openFiles.clear(); config.openFiles.clear();
}); });
@ -386,7 +355,7 @@ ox::Error StudioUI::openFile(ox::StringViewCR path) noexcept {
return openFileActiveTab(path, true); return openFileActiveTab(path, true);
} }
ox::Error StudioUI::openFileActiveTab(ox::StringViewCR path, bool const makeActiveTab) noexcept { ox::Error StudioUI::openFileActiveTab(ox::StringViewCR path, bool makeActiveTab) noexcept {
if (!m_project) { if (!m_project) {
return ox::Error(1, "No project open to open a file from"); return ox::Error(1, "No project open to open a file from");
} }
@ -400,7 +369,7 @@ ox::Error StudioUI::openFileActiveTab(ox::StringViewCR path, bool const makeActi
} }
return {}; return {};
} }
OX_REQUIRE(ext, fileExt(path)); OX_REQUIRE(ext, studio::fileExt(path));
// create Editor // create Editor
BaseEditor *editor = nullptr; BaseEditor *editor = nullptr;
auto const err = m_editorMakers.contains(ext) ? auto const err = m_editorMakers.contains(ext) ?
@ -410,7 +379,7 @@ ox::Error StudioUI::openFileActiveTab(ox::StringViewCR path, bool const makeActi
if constexpr(!ox::defines::Debug) { if constexpr(!ox::defines::Debug) {
oxErrf("Could not open Editor: {}\n", toStr(err)); oxErrf("Could not open Editor: {}\n", toStr(err));
} else { } else {
oxErrf("Could not open Editor: {} ({}:{})\n", err.errCode, err.src.file_name(), err.src.line()); oxErrf("Could not open Editor: {} ({}:{})\n", err.errCode, err.file, err.line);
} }
return err; return err;
} }
@ -422,7 +391,7 @@ ox::Error StudioUI::openFileActiveTab(ox::StringViewCR path, bool const makeActi
m_activeEditorUpdatePending = editor; m_activeEditorUpdatePending = editor;
} }
// save to config // save to config
studio::editConfig<StudioConfig>(keelCtx(m_tctx), [&path](StudioConfig &config) { studio::editConfig<StudioConfig>(keelCtx(m_ctx), [&path](StudioConfig &config) {
if (!config.openFiles.contains(path)) { if (!config.openFiles.contains(path)) {
config.openFiles.emplace_back(path); config.openFiles.emplace_back(path);
} }
@ -436,7 +405,7 @@ ox::Error StudioUI::closeFile(ox::StringViewCR path) noexcept {
} }
std::ignore = m_openFiles.erase(std::remove(m_openFiles.begin(), m_openFiles.end(), path)); std::ignore = m_openFiles.erase(std::remove(m_openFiles.begin(), m_openFiles.end(), path));
// save to config // save to config
studio::editConfig<StudioConfig>(keelCtx(m_tctx), [&](StudioConfig &config) { studio::editConfig<StudioConfig>(keelCtx(m_ctx), [&](StudioConfig &config) {
std::ignore = config.openFiles.erase(std::remove(config.openFiles.begin(), config.openFiles.end(), path)); std::ignore = config.openFiles.erase(std::remove(config.openFiles.begin(), config.openFiles.end(), path));
}); });
return {}; return {};

View File

@ -12,9 +12,7 @@
#include <studio/module.hpp> #include <studio/module.hpp>
#include <studio/project.hpp> #include <studio/project.hpp>
#include <studio/task.hpp> #include <studio/task.hpp>
#include "aboutpopup.hpp" #include "aboutpopup.hpp"
#include "deleteconfirmation.hpp"
#include "newmenu.hpp" #include "newmenu.hpp"
#include "newproject.hpp" #include "newproject.hpp"
#include "projectexplorer.hpp" #include "projectexplorer.hpp"
@ -26,28 +24,26 @@ class StudioUI: public ox::SignalHandler {
friend class StudioUIDrawer; friend class StudioUIDrawer;
private: private:
StudioContext m_sctx; studio::StudioContext m_sctx;
turbine::Context &m_tctx; turbine::Context &m_ctx;
ox::String m_projectDataDir; ox::String m_projectDataDir;
ox::UPtr<Project> m_project; ox::UPtr<studio::Project> m_project;
TaskRunner m_taskRunner; studio::TaskRunner m_taskRunner;
ox::Vector<ox::UPtr<BaseEditor>> m_editors; ox::Vector<ox::UPtr<studio::BaseEditor>> m_editors;
ox::Vector<ox::UPtr<Widget>> m_widgets; ox::Vector<ox::UPtr<studio::Widget>> m_widgets;
ox::HashMap<ox::String, EditorMaker::Func> m_editorMakers; ox::HashMap<ox::String, studio::EditorMaker::Func> m_editorMakers;
ProjectExplorer m_projectExplorer; ProjectExplorer m_projectExplorer;
ox::Vector<ox::String> m_openFiles; ox::Vector<ox::String> m_openFiles;
BaseEditor *m_activeEditorOnLastDraw = nullptr; studio::BaseEditor *m_activeEditorOnLastDraw = nullptr;
BaseEditor *m_activeEditor = nullptr; studio::BaseEditor *m_activeEditor = nullptr;
BaseEditor *m_activeEditorUpdatePending = nullptr; studio::BaseEditor *m_activeEditorUpdatePending = nullptr;
NewMenu m_newMenu; NewMenu m_newMenu;
DeleteConfirmation m_deleteConfirmation{m_sctx};
NewProject m_newProject; NewProject m_newProject;
AboutPopup m_aboutPopup; AboutPopup m_aboutPopup;
ox::Array<Popup*, 4> const m_popups = { ox::Array<studio::Popup*, 3> const m_popups = {
&m_newMenu, &m_newMenu,
&m_newProject, &m_newProject,
&m_aboutPopup, &m_aboutPopup
&m_deleteConfirmation,
}; };
bool m_showProjectExplorer = true; bool m_showProjectExplorer = true;
@ -57,7 +53,7 @@ class StudioUI: public ox::SignalHandler {
void handleKeyEvent(turbine::Key, bool down) noexcept; void handleKeyEvent(turbine::Key, bool down) noexcept;
[[nodiscard]] [[nodiscard]]
constexpr Project *project() noexcept { constexpr studio::Project *project() noexcept {
return m_project.get(); return m_project.get();
} }
@ -71,9 +67,9 @@ class StudioUI: public ox::SignalHandler {
void drawTabs() noexcept; void drawTabs() noexcept;
void loadEditorMaker(EditorMaker const&editorMaker) noexcept; void loadEditorMaker(studio::EditorMaker const&editorMaker) noexcept;
void loadModule(Module const&mod) noexcept; void loadModule(studio::Module const&mod) noexcept;
void loadModules() noexcept; void loadModules() noexcept;
@ -87,10 +83,6 @@ class StudioUI: public ox::SignalHandler {
void handleKeyInput() noexcept; void handleKeyInput() noexcept;
ox::Error addFile(ox::StringViewCR path) noexcept;
ox::Error deleteFile(ox::StringViewCR path) noexcept;
ox::Error createOpenProject(ox::StringViewCR path) noexcept; ox::Error createOpenProject(ox::StringViewCR path) noexcept;
ox::Error openProjectPath(ox::StringParam path) noexcept; ox::Error openProjectPath(ox::StringParam path) noexcept;

View File

@ -214,8 +214,7 @@ bool ListBox(
ox::CStringViewCR name, ox::CStringViewCR name,
std::function<ox::CStringView(size_t)> const&f, std::function<ox::CStringView(size_t)> const&f,
size_t strCnt, size_t strCnt,
size_t &selIdx, size_t &selIdx) noexcept;
ImVec2 const&sz = {0, 0}) noexcept;
/** /**
* *

View File

@ -4,9 +4,7 @@
#pragma once #pragma once
#include <algorithm> #include <ox/claw/claw.hpp>
#include <ox/model/typenamecatcher.hpp>
#include <keel/media.hpp> #include <keel/media.hpp>
#include <turbine/context.hpp> #include <turbine/context.hpp>
@ -15,249 +13,75 @@
namespace studio { namespace studio {
class ItemTemplate {
private:
ox::String const m_name{"Default"};
public:
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 { 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: public:
constexpr ItemMaker( ox::String const typeName;
ox::String const parentDir;
ox::String const fileExt;
constexpr explicit ItemMaker(
ox::StringParam pName, ox::StringParam pName,
ox::StringParam pParentDir, ox::StringParam pParentDir,
ox::StringParam pFileExt) noexcept: ox::StringParam pFileExt) noexcept:
m_parentDir{std::move(pParentDir)}, typeName{std::move(pName)},
m_fileExt{std::move(pFileExt)}, parentDir{std::move(pParentDir)},
m_typeDisplayName{std::move(pName)} { fileExt{std::move(pFileExt)} {
} }
virtual ~ItemMaker() noexcept = default; virtual ~ItemMaker() noexcept = default;
[[nodiscard]] [[nodiscard]]
ox::String const&typeDisplayName() const noexcept { virtual ox::String itemPath(ox::StringView pName) const noexcept {
return m_typeDisplayName; return ox::sfmt("/{}/{}.{}", parentDir, pName, fileExt);
}
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. * Returns path of the file created.
* @param ctx * @param ctx
* @param pPath
* @param pName * @param pName
* @param pTemplateIdx
* @return path of file or error in Result * @return path of file or error in Result
*/ */
ox::Error write( virtual ox::Result<ox::String> write(StudioContext &ctx, ox::StringView pName) const noexcept = 0;
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> template<typename T>
class ItemMakerT final: public ItemMaker { class ItemMakerT: public ItemMaker {
private: private:
T const m_item;
ox::ClawFormat const m_fmt; ox::ClawFormat const m_fmt;
public: public:
constexpr ItemMakerT( constexpr ItemMakerT(
ox::StringParam pDisplayName, ox::StringParam pDisplayName,
ox::StringParam pParentDir, ox::StringParam pParentDir,
ox::StringParam fileExt, ox::StringParam fileExt,
ox::ClawFormat const pFmt = ox::ClawFormat::Metal) noexcept: ox::ClawFormat pFmt = ox::ClawFormat::Metal) noexcept:
ItemMaker( ItemMaker(std::move(pDisplayName), std::move(pParentDir), std::move(fileExt)),
std::move(pDisplayName), m_fmt(pFmt) {
std::move(pParentDir),
std::move(fileExt)),
m_fmt{pFmt} {
installTemplate(ox::make_unique<ItemTemplateT<T>>());
} }
constexpr ItemMakerT( constexpr ItemMakerT(
ox::StringParam pDisplayName, ox::StringParam pDisplayName,
ox::StringParam pParentDir, ox::StringParam pParentDir,
ox::StringParam fileExt, ox::StringParam fileExt,
T const&pItem, T pItem,
ox::ClawFormat const pFmt) noexcept: ox::ClawFormat pFmt) noexcept:
ItemMaker( ItemMaker(std::move(pDisplayName), std::move(pParentDir), std::move(fileExt)),
std::move(pDisplayName), m_item(std::move(pItem)),
std::move(pParentDir), m_fmt(pFmt) {
std::move(fileExt)),
m_fmt{pFmt} {
installTemplate(ox::make_unique<ItemTemplateT<T>>(std::move(pItem)));
} }
constexpr ItemMakerT( constexpr ItemMakerT(
ox::StringParam pDisplayName, ox::StringParam pDisplayName,
ox::StringParam pParentDir, ox::StringParam pParentDir,
ox::StringParam fileExt, ox::StringParam fileExt,
T &&pItem, T &&pItem,
ox::ClawFormat const pFmt) noexcept: ox::ClawFormat pFmt) noexcept:
ItemMaker( ItemMaker(std::move(pDisplayName), std::move(pParentDir), std::move(fileExt)),
std::move(pDisplayName), m_item(std::move(pItem)),
std::move(pParentDir), m_fmt(pFmt) {
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 {
ox::StringView typeName() const noexcept override { auto const path = itemPath(pName);
return ox::ModelTypeName_v<T>; createUuidMapping(keelCtx(sctx.tctx), path, ox::UUID::generate().unwrap());
OX_RETURN_ERROR(sctx.project->writeObj(path, m_item, m_fmt));
return path;
} }
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 {};
}
}; };
} }

View File

@ -31,8 +31,6 @@ class Module {
virtual ox::Vector<ox::UPtr<ItemMaker>> itemMakers(studio::StudioContext&) const; virtual ox::Vector<ox::UPtr<ItemMaker>> itemMakers(studio::StudioContext&) const;
virtual ox::Vector<ox::UPtr<ItemTemplate>> itemTemplates(studio::StudioContext&) const;
}; };
template<typename Editor> template<typename Editor>

View File

@ -47,7 +47,7 @@ class Popup {
return m_title; 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);
}; };

View File

@ -45,7 +45,7 @@ constexpr ox::StringView parentDir(ox::StringView path) noexcept {
return substr(path, 0, extStart); return substr(path, 0, extStart);
} }
class Project: public ox::SignalHandler { class Project {
private: private:
ox::SmallMap<ox::String, ox::Optional<ox::ClawFormat>> m_typeFmt; ox::SmallMap<ox::String, ox::Optional<ox::ClawFormat>> m_typeFmt;
keel::Context &m_ctx; keel::Context &m_ctx;
@ -91,8 +91,6 @@ class Project: public ox::SignalHandler {
ox::Result<ox::FileStat> stat(ox::StringViewCR path) const noexcept; ox::Result<ox::FileStat> stat(ox::StringViewCR path) const noexcept;
ox::Error deleteItem(ox::StringViewCR path) noexcept;
[[nodiscard]] [[nodiscard]]
bool exists(ox::StringViewCR path) const noexcept; bool exists(ox::StringViewCR path) const noexcept;

View File

@ -13,7 +13,7 @@ namespace studio {
class NoChangesException: public ox::Exception { class NoChangesException: public ox::Exception {
public: public:
inline NoChangesException(std::source_location sloc = std::source_location::current()): inline NoChangesException(std::source_location sloc = std::source_location::current()):
ox::Exception(1, "Command makes no changes.", sloc) {} ox::Exception(sloc.file_name(), sloc.line(), 1, "Command makes no changes.") {}
}; };
class UndoCommand { class UndoCommand {

View File

@ -46,7 +46,7 @@ void BaseEditor::save() noexcept {
setUnsavedChanges(false); setUnsavedChanges(false);
} else { } else {
if constexpr(ox::defines::Debug) { if constexpr(ox::defines::Debug) {
oxErrorf("Could not save file {}: {} ({}:{})", itemPath(), toStr(err), err.src.file_name(), err.src.line()); oxErrorf("Could not save file {}: {} ({}:{})", itemPath(), toStr(err), err.file, err.line);
} else { } else {
oxErrorf("Could not save file {}: {}", itemPath(), toStr(err)); oxErrorf("Could not save file {}: {}", itemPath(), toStr(err));
} }

View File

@ -136,12 +136,11 @@ bool FileComboBox(
bool ListBox( bool ListBox(
ox::CStringViewCR name, 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 strCnt,
size_t &selIdx, size_t &selIdx) noexcept {
ImVec2 const&sz) noexcept {
auto out = false; auto out = false;
if (ImGui::BeginListBox(name.c_str(), sz)) { if (ImGui::BeginListBox(name.c_str())) {
for (size_t i = 0; i < strCnt; ++i) { for (auto i = 0u; i < strCnt; ++i) {
auto str = f(i); auto str = f(i);
ig::IDStackItem const idStackItem2(static_cast<int>(i)); ig::IDStackItem const idStackItem2(static_cast<int>(i));
if (ImGui::Selectable(str.c_str(), selIdx == i)) { if (ImGui::Selectable(str.c_str(), selIdx == i)) {
@ -162,12 +161,6 @@ bool ListBox(ox::CStringViewCR name, ox::SpanView<ox::String> const&list, size_t
}, list.size(), selIdx); }, 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( FilePicker::FilePicker(
StudioContext &sctx, StudioContext &sctx,

View File

@ -6,15 +6,11 @@
namespace studio { namespace studio {
ox::Vector<EditorMaker> Module::editors(StudioContext&) const { ox::Vector<EditorMaker> Module::editors(studio::StudioContext&) const {
return {}; return {};
} }
ox::Vector<ox::UPtr<ItemMaker>> Module::itemMakers(StudioContext&) const { ox::Vector<ox::UPtr<ItemMaker>> Module::itemMakers(studio::StudioContext&) const {
return {};
}
ox::Vector<ox::UPtr<ItemTemplate>> Module::itemTemplates(StudioContext&) const {
return {}; return {};
} }

View File

@ -7,11 +7,11 @@
namespace studio { 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); studio::ig::centerNextWindow(ctx);
ImGui::SetNextWindowSize(static_cast<ImVec2>(m_size)); ImGui::SetNextWindowSize(static_cast<ImVec2>(m_size));
constexpr auto modalFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize; 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(); drawContents();
ImGui::EndPopup(); ImGui::EndPopup();
} }

View File

@ -69,14 +69,6 @@ ox::Result<ox::FileStat> Project::stat(ox::StringViewCR path) const noexcept {
return m_fs.stat(path); return m_fs.stat(path);
} }
ox::Error Project::deleteItem(ox::StringViewCR path) noexcept {
auto const err = m_fs.remove(path);
if (!err) {
fileDeleted.emit(path);
}
return err;
}
bool Project::exists(ox::StringViewCR path) const noexcept { bool Project::exists(ox::StringViewCR path) const noexcept {
return m_fs.stat(path).error == 0; return m_fs.stat(path).error == 0;
} }

View File

@ -19,7 +19,7 @@ target_include_directories(
) )
target_sources( target_sources(
Turbine PRIVATE Turbine PUBLIC
turbine.cpp turbine.cpp
) )