Compare commits
44 Commits
312097a799
...
release-d2
| Author | SHA1 | Date | |
|---|---|---|---|
| 1057099fcf | |||
| 17577b6e8b | |||
| db442e2775 | |||
| 23cd50d29e | |||
| 28088d1ed1 | |||
| baf5fa3199 | |||
| 857587c18b | |||
| eb3d53c955 | |||
| 14d58f3f5b | |||
| 5f2397903a | |||
| 58e0ecb469 | |||
| 8838bf420e | |||
| bddc544d7c | |||
| a9437191bf | |||
| 9d8da7ccda | |||
| 394b568e72 | |||
| 78e9f70db6 | |||
| 12e5623fe6 | |||
| cfdfb0a8c9 | |||
| 56e665301f | |||
| 7415ce4bd9 | |||
| 05f42150a1 | |||
| 8ea2bc6934 | |||
| c780924122 | |||
| 8c538560ca | |||
| c3e75bdb55 | |||
| e78c405046 | |||
| 6b7c5d896e | |||
| a33a73d73a | |||
| e1cfcc8b5f | |||
| 514cb97835 | |||
| 4b5218c4f1 | |||
| 2ca77173d3 | |||
| cce5f52f96 | |||
| b55993294a | |||
| d8f847d707 | |||
| 8419b137e5 | |||
| ed1160ec74 | |||
| 1e21778059 | |||
| 78379f58c8 | |||
| 4322f720ba | |||
| 26f1a6051f | |||
| c4c1d4777b | |||
| fab012d3e7 |
22
Makefile
22
Makefile
@@ -52,3 +52,25 @@ configure-gba:
|
||||
.PHONY: configure-gba-debug
|
||||
configure-gba-debug:
|
||||
${BC_CMD_SETUP_BUILD} --toolchain=deps/gbabuildcore/cmake/modules/GBA.cmake --target=gba --current_build=0 --build_type=debug --build_root=${BC_VAR_BUILD_PATH}
|
||||
|
||||
.PHONY: loc
|
||||
loc:
|
||||
${BC_PY3} util/scripts/loc.py \
|
||||
--search-dirs \
|
||||
src \
|
||||
deps/ox/src \
|
||||
deps/buildcore \
|
||||
deps/gbabuildcore \
|
||||
deps/glutils \
|
||||
deps/teagba \
|
||||
--include-exts \
|
||||
.cpp \
|
||||
.hpp \
|
||||
.py \
|
||||
.s \
|
||||
.cmake \
|
||||
--exclude-paths \
|
||||
deps/teagba/src/gba_crt0.s \
|
||||
src/olympic/studio/applib/src/font.cpp \
|
||||
src/olympic/studio/applib/src/font.hpp \
|
||||
src/nostalgia/studio/icondata.cpp
|
||||
|
||||
2
deps/ox/src/ox/claw/write.hpp
vendored
2
deps/ox/src/ox/claw/write.hpp
vendored
@@ -39,7 +39,7 @@ struct TypeInfoCatcher {
|
||||
}
|
||||
|
||||
constexpr Error field(...) noexcept {
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
static constexpr auto opType() {
|
||||
|
||||
4
deps/ox/src/ox/event/signal.hpp
vendored
4
deps/ox/src/ox/event/signal.hpp
vendored
@@ -218,7 +218,7 @@ Error Signal<Args...>::emitCheckError(Args... args) const noexcept {
|
||||
for (auto &f : m_slots) {
|
||||
f->call(args...);
|
||||
}
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
} catch (const ox::Exception &ex) {
|
||||
return ox::Error(ex.errCode, ex.msg, ex.src);
|
||||
}
|
||||
@@ -410,7 +410,7 @@ Error Signal<Error(Args...)>::emitCheckError(Args... args) const noexcept {
|
||||
for (auto &f : m_slots) {
|
||||
OX_RETURN_ERROR(f->call(ox::forward<Args>(args)...));
|
||||
}
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -230,7 +230,7 @@ template<typename size_t>
|
||||
Error FileStoreTemplate<size_t>::incLinks(uint64_t id) {
|
||||
OX_REQUIRE_M(item, find(static_cast<size_t>(id)).validate());
|
||||
++item->links;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename size_t>
|
||||
@@ -240,7 +240,7 @@ Error FileStoreTemplate<size_t>::decLinks(uint64_t id) {
|
||||
if (item->links == 0) {
|
||||
OX_RETURN_ERROR(remove(item));
|
||||
}
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename size_t>
|
||||
@@ -298,7 +298,7 @@ Error FileStoreTemplate<size_t>::write(uint64_t id64, const void *data, FsSize_t
|
||||
dest->id.get(), dest.offset(), destData.size());
|
||||
fsData->rootNode = dest.offset();
|
||||
oxTracef("ox.fs.FileStoreTemplate.write", "Root inode: {}", dest->id.get());
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
} else {
|
||||
oxTrace("ox.fs.FileStoreTemplate.write.fail", "Could not place item due to absence of FileStore header.");
|
||||
@@ -427,7 +427,7 @@ Error FileStoreTemplate<size_t>::resize() {
|
||||
oxTracef("ox.fs.FileStoreTemplate.resize", "resize to: {}", newSize);
|
||||
OX_RETURN_ERROR(m_buffer->setSize(newSize));
|
||||
oxTracef("ox.fs.FileStoreTemplate.resize", "resized to: {}", m_buffer->size());
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename size_t>
|
||||
@@ -479,7 +479,7 @@ Error FileStoreTemplate<size_t>::walk(Error(*cb)(uint8_t, uint64_t, uint64_t)) {
|
||||
for (auto i = m_buffer->iterator(); i.valid(); i.next()) {
|
||||
OX_RETURN_ERROR(cb(i->fileType, i.ptr().offset(), i.ptr().end()));
|
||||
}
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename size_t>
|
||||
@@ -503,7 +503,7 @@ Error FileStoreTemplate<size_t>::compact() {
|
||||
return m_buffer->compact([this, &isFirstItem](uint64_t oldAddr, ItemPtr item) -> Error {
|
||||
if (isFirstItem) {
|
||||
isFirstItem = false;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
if (!item.valid()) {
|
||||
return ox::Error(1);
|
||||
@@ -524,7 +524,7 @@ Error FileStoreTemplate<size_t>::compact() {
|
||||
parent->right = item;
|
||||
}
|
||||
}
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -552,7 +552,7 @@ Error FileStoreTemplate<size_t>::placeItem(ItemPtr item) {
|
||||
item->left = root->left;
|
||||
item->right = root->right;
|
||||
oxTracef("ox.fs.FileStoreTemplate.placeItem", "Overwrote Root Item: {}", item->id.get());
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
} else {
|
||||
return placeItem(root, item);
|
||||
}
|
||||
@@ -573,7 +573,7 @@ Error FileStoreTemplate<size_t>::placeItem(ItemPtr root, ItemPtr item, int depth
|
||||
item->right = right->right;
|
||||
}
|
||||
oxTracef("ox.fs.FileStoreTemplate.placeItem", "Placed Item: {}", item->id.get());
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
} else {
|
||||
return placeItem(right, item, depth + 1);
|
||||
}
|
||||
@@ -586,7 +586,7 @@ Error FileStoreTemplate<size_t>::placeItem(ItemPtr root, ItemPtr item, int depth
|
||||
item->right = left->right;
|
||||
}
|
||||
oxTracef("ox.fs.FileStoreTemplate.placeItem", "Placed Item: {}", item->id.get());
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
} else {
|
||||
return placeItem(left, item, depth + 1);
|
||||
}
|
||||
@@ -624,7 +624,7 @@ Error FileStoreTemplate<size_t>::unplaceItem(ItemPtr item) {
|
||||
} else {
|
||||
fsData->rootNode = 0;
|
||||
}
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
} else {
|
||||
return unplaceItem(root, item);
|
||||
}
|
||||
@@ -661,7 +661,7 @@ Error FileStoreTemplate<size_t>::unplaceItem(ItemPtr root, ItemPtr item, int dep
|
||||
if (item->left) {
|
||||
OX_RETURN_ERROR(placeItem(m_buffer->ptr(item->left)));
|
||||
}
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename size_t>
|
||||
@@ -669,7 +669,7 @@ Error FileStoreTemplate<size_t>::remove(ItemPtr item) {
|
||||
if (item.valid()) {
|
||||
OX_RETURN_ERROR(unplaceItem(item));
|
||||
OX_RETURN_ERROR(m_buffer->free(item));
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
return ox::Error(1);
|
||||
}
|
||||
|
||||
8
deps/ox/src/ox/fs/filesystem/filesystem.hpp
vendored
8
deps/ox/src/ox/fs/filesystem/filesystem.hpp
vendored
@@ -329,7 +329,7 @@ Error FileSystemTemplate<FileStore, Directory>::format(void *buff, uint64_t buff
|
||||
return ox::Error(1);
|
||||
}
|
||||
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename FileStore, typename Directory>
|
||||
@@ -346,7 +346,7 @@ Error FileSystemTemplate<FileStore, Directory>::move(StringViewCR src, StringVie
|
||||
OX_REQUIRE_M(inode, rootDir.find(src));
|
||||
OX_RETURN_ERROR(rootDir.write(dest, inode));
|
||||
OX_RETURN_ERROR(rootDir.remove(src));
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename FileStore, typename Directory>
|
||||
@@ -407,7 +407,7 @@ Error FileSystemTemplate<FileStore, Directory>::removePath(StringViewCR path, bo
|
||||
oxTrace("FileSystemTemplate.remove.fail", "Tried to remove directory without recursive setting.");
|
||||
return ox::Error(1);
|
||||
}
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename FileStore, typename Directory>
|
||||
@@ -424,7 +424,7 @@ Result<Vector<String>> FileSystemTemplate<FileStore, Directory>::ls(StringViewCR
|
||||
Vector<String> out;
|
||||
OX_RETURN_ERROR(ls(path, [&out](StringViewCR name, typename FileStore::InodeId_t) {
|
||||
out.emplace_back(name);
|
||||
return ox::Error(0);
|
||||
return ox::Error{};
|
||||
}));
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ Error PassThroughFS::move(StringViewCR src, StringViewCR dest) noexcept {
|
||||
if (ec.value()) {
|
||||
return ox::Error(1);
|
||||
}
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<Vector<String>> PassThroughFS::ls(StringViewCR dir) const noexcept {
|
||||
@@ -148,7 +148,7 @@ Error PassThroughFS::readFilePath(StringViewCR path, void *buffer, std::size_t b
|
||||
oxTracef("ox.fs.PassThroughFS.read.error", "Read of {} failed: {}", path, f.what());
|
||||
return ox::Error(2);
|
||||
}
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
Error PassThroughFS::readFileInode(uint64_t, void*, std::size_t) noexcept {
|
||||
@@ -197,7 +197,7 @@ Error PassThroughFS::writeFilePath(StringViewCR path, const void *buffer, uint64
|
||||
oxTracef("ox.fs.PassThroughFS.read.error", "Write of {} failed: {}", path, f.what());
|
||||
return ox::Error(1);
|
||||
}
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
Error PassThroughFS::writeFileInode(uint64_t, const void*, uint64_t, FileType) noexcept {
|
||||
|
||||
@@ -99,7 +99,7 @@ Error PassThroughFS::ls(StringViewCR dir, F cb) const noexcept {
|
||||
for (auto &p : di) {
|
||||
OX_RETURN_ERROR(cb(p.path().filename().c_str(), 0));
|
||||
}
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ Error PathIterator::dirPath(char *out, std::size_t outSize) {
|
||||
if (idx >= 0 && size < outSize) {
|
||||
ox::memcpy(out, m_path, size);
|
||||
out[size] = 0;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
} else {
|
||||
return ox::Error(1);
|
||||
}
|
||||
@@ -85,7 +85,7 @@ Error PathIterator::next(StringView &fileName) {
|
||||
std::size_t size = 0;
|
||||
auto retval = ox::Error(1);
|
||||
if (m_iterator < m_maxSize && ox::strlen(&m_path[m_iterator])) {
|
||||
retval = ox::Error(0);
|
||||
retval = {};
|
||||
if (m_path[m_iterator] == '/') {
|
||||
m_iterator++;
|
||||
}
|
||||
@@ -118,7 +118,7 @@ Result<std::size_t> PathIterator::nextSize() const {
|
||||
auto retval = ox::Error(1);
|
||||
auto it = m_iterator;
|
||||
if (it < m_maxSize && ox::strlen(&m_path[it])) {
|
||||
retval = ox::Error(0);
|
||||
retval = {};
|
||||
if (m_path[it] == '/') {
|
||||
it++;
|
||||
}
|
||||
|
||||
6
deps/ox/src/ox/fs/ptrarith/nodebuffer.hpp
vendored
6
deps/ox/src/ox/fs/ptrarith/nodebuffer.hpp
vendored
@@ -354,7 +354,7 @@ Error NodeBuffer<size_t, Item>::free(ItemPtr item) noexcept {
|
||||
}
|
||||
}
|
||||
m_header.bytesUsed -= item.size();
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename size_t, typename Item>
|
||||
@@ -370,7 +370,7 @@ Error NodeBuffer<size_t, Item>::setSize(std::size_t size) noexcept {
|
||||
m_header.size = static_cast<size_t>(size);
|
||||
auto data = reinterpret_cast<uint8_t*>(this) + end;
|
||||
ox::memset(data, 0, size - end);
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -422,7 +422,7 @@ Error NodeBuffer<size_t, Item>::compact(F cb) noexcept {
|
||||
src = ptr(dest->next);
|
||||
dest = uninitializedPtr(dest.offset() + dest->fullSize());
|
||||
}
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename size_t, typename Item>
|
||||
|
||||
4
deps/ox/src/ox/fs/tool.cpp
vendored
4
deps/ox/src/ox/fs/tool.cpp
vendored
@@ -48,7 +48,7 @@ static ox::Error runLs(ox::FileSystem *fs, ox::Span<const char*> args) noexcept
|
||||
for (const auto &file : files) {
|
||||
oxOutf("{}\n", file);
|
||||
}
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
static ox::Error runRead(ox::FileSystem *fs, ox::Span<const char*> args) noexcept {
|
||||
@@ -60,7 +60,7 @@ static ox::Error runRead(ox::FileSystem *fs, ox::Span<const char*> args) noexcep
|
||||
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
||||
std::ignore = fwrite(buff.data(), sizeof(decltype(buff)::value_type), buff.size(), stdout);
|
||||
OX_ALLOW_UNSAFE_BUFFERS_END
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
static ox::Error run(int argc, const char **argv) noexcept {
|
||||
|
||||
31
deps/ox/src/ox/logconn/logconn.cpp
vendored
31
deps/ox/src/ox/logconn/logconn.cpp
vendored
@@ -91,23 +91,28 @@ ox::Error LoggerConn::sendInit(const InitTraceMsg &msg) noexcept {
|
||||
}
|
||||
|
||||
void LoggerConn::msgSend() noexcept {
|
||||
while (true) {
|
||||
std::unique_lock lk(m_waitMut);
|
||||
m_waitCond.wait(lk);
|
||||
if (!m_running) {
|
||||
break;
|
||||
}
|
||||
std::lock_guard const buffLk(m_buffMut);
|
||||
try {
|
||||
while (true) {
|
||||
Array<char, units::KB> tmp;
|
||||
const auto read = m_buff.read(tmp.data(), tmp.size());
|
||||
if (!read) {
|
||||
std::unique_lock lk(m_waitMut);
|
||||
m_waitCond.wait(lk);
|
||||
if (!m_running) {
|
||||
break;
|
||||
}
|
||||
oxAssert(read <= tmp.size(), "logger trying to read too much data");
|
||||
//std::printf("LoggerConn: sending %lu bytes\n", read);
|
||||
std::ignore = send(tmp.data(), read);
|
||||
std::lock_guard const buffLk(m_buffMut);
|
||||
while (true) {
|
||||
Array<char, units::KB> tmp;
|
||||
const auto read = m_buff.read(tmp.data(), tmp.size());
|
||||
if (!read) {
|
||||
break;
|
||||
}
|
||||
oxAssert(read <= tmp.size(), "logger trying to read too much data");
|
||||
//std::printf("LoggerConn: sending %lu bytes\n", read);
|
||||
std::ignore = send(tmp.data(), read);
|
||||
}
|
||||
}
|
||||
} catch (std::exception const &e) {
|
||||
oxErrf("Exception in logger thread: {}\n", e.what());
|
||||
oxAssert(false, "logger thread exception");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
16
deps/ox/src/ox/mc/read.hpp
vendored
16
deps/ox/src/ox/mc/read.hpp
vendored
@@ -197,7 +197,7 @@ constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, bool *val) n
|
||||
OX_RETURN_ERROR(result);
|
||||
}
|
||||
++m_field;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
// array handler
|
||||
@@ -256,7 +256,7 @@ constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, HashMap<Stri
|
||||
}
|
||||
}
|
||||
++m_field;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
@@ -330,7 +330,7 @@ constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, BasicString<
|
||||
}
|
||||
}
|
||||
++m_field;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
@@ -370,7 +370,7 @@ constexpr Error MetalClawReaderTemplate<Reader>::fieldCString(const char*, char
|
||||
data[size] = 0;
|
||||
}
|
||||
++m_field;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
@@ -388,7 +388,7 @@ constexpr Error MetalClawReaderTemplate<Reader>::fieldCString(const char*, char
|
||||
data[size] = 0;
|
||||
}
|
||||
++m_field;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
@@ -416,7 +416,7 @@ constexpr Error MetalClawReaderTemplate<Reader>::fieldCString(const char*, char
|
||||
}
|
||||
}
|
||||
++m_field;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
@@ -464,7 +464,7 @@ constexpr Error MetalClawReaderTemplate<Reader>::readInteger(I *val) noexcept {
|
||||
}
|
||||
}
|
||||
++m_field;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
@@ -487,7 +487,7 @@ constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, CB cb) noexc
|
||||
}
|
||||
}
|
||||
++m_field;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
|
||||
14
deps/ox/src/ox/mc/write.hpp
vendored
14
deps/ox/src/ox/mc/write.hpp
vendored
@@ -122,7 +122,7 @@ class MetalClawWriter {
|
||||
}
|
||||
OX_RETURN_ERROR(m_fieldPresence.set(static_cast<std::size_t>(m_field), fieldSet));
|
||||
++m_field;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
};
|
||||
@@ -184,7 +184,7 @@ constexpr Error MetalClawWriter<Writer>::field(const char*, const bool *val) noe
|
||||
OX_RETURN_ERROR(m_fieldPresence.set(static_cast<std::size_t>(m_field), *val));
|
||||
}
|
||||
++m_field;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Writer_c Writer>
|
||||
@@ -201,7 +201,7 @@ constexpr Error MetalClawWriter<Writer>::field(const char*, const BasicString<Sm
|
||||
}
|
||||
OX_RETURN_ERROR(m_fieldPresence.set(static_cast<std::size_t>(m_field), fieldSet));
|
||||
++m_field;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Writer_c Writer>
|
||||
@@ -229,7 +229,7 @@ constexpr Error MetalClawWriter<Writer>::fieldCString(const char*, const char *c
|
||||
}
|
||||
OX_RETURN_ERROR(m_fieldPresence.set(static_cast<std::size_t>(m_field), fieldSet));
|
||||
++m_field;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Writer_c Writer>
|
||||
@@ -255,7 +255,7 @@ constexpr Error MetalClawWriter<Writer>::fieldCString(const char*, const char *v
|
||||
}
|
||||
OX_RETURN_ERROR(m_fieldPresence.set(static_cast<std::size_t>(m_field), fieldSet));
|
||||
++m_field;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Writer_c Writer>
|
||||
@@ -319,7 +319,7 @@ OX_ALLOW_UNSAFE_BUFFERS_END
|
||||
}
|
||||
OX_RETURN_ERROR(m_fieldPresence.set(static_cast<std::size_t>(m_field), fieldSet));
|
||||
++m_field;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Writer_c Writer>
|
||||
@@ -356,7 +356,7 @@ constexpr Error MetalClawWriter<Writer>::field(const char*, const HashMap<String
|
||||
}
|
||||
OX_RETURN_ERROR(m_fieldPresence.set(static_cast<std::size_t>(m_field), fieldSet));
|
||||
++m_field;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Writer_c Writer>
|
||||
|
||||
2
deps/ox/src/ox/model/def.hpp
vendored
2
deps/ox/src/ox/model/def.hpp
vendored
@@ -13,7 +13,7 @@
|
||||
// oxModelFwdDecl is necessary because Apple-Clang is broken...
|
||||
#define OX_MODEL_FWD_DECL(modelName) constexpr ox::Error model(auto *io, ox::CommonPtrWith<modelName> auto *o) noexcept
|
||||
#define OX_MODEL_BEGIN(modelName) constexpr ox::Error model(auto *io, [[maybe_unused]] ox::CommonPtrWith<modelName> auto *o) noexcept { OX_RETURN_ERROR(io->template setTypeInfo<modelName>());
|
||||
#define OX_MODEL_END() return ox::Error(0); }
|
||||
#define OX_MODEL_END() return {}; }
|
||||
#define OX_MODEL_FIELD(fieldName) OX_RETURN_ERROR(io->field(#fieldName, &o->fieldName));
|
||||
#define OX_MODEL_FIELD_RENAME(objFieldName, serFieldName) OX_RETURN_ERROR(io->field(#serFieldName, &o->objFieldName));
|
||||
#define OX_MODEL_FRIEND(modelName) friend constexpr ox::Error model(auto *io, ox::CommonPtrWith<modelName> auto *o) noexcept
|
||||
|
||||
2
deps/ox/src/ox/model/desctypes.hpp
vendored
2
deps/ox/src/ox/model/desctypes.hpp
vendored
@@ -244,7 +244,7 @@ constexpr Error model(TypeDescReader<T> *io, CommonPtrWith<DescriptorField> auto
|
||||
// defaultValue is unused now, but placeholder for backwards compatibility
|
||||
int defaultValue = 0;
|
||||
oxReturnError(io->field("defaultValue", &defaultValue));
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
6
deps/ox/src/ox/model/descwrite.hpp
vendored
6
deps/ox/src/ox/model/descwrite.hpp
vendored
@@ -205,7 +205,7 @@ constexpr Error TypeDescWriter::field(StringViewCR name, T const*, std::size_t,
|
||||
const auto t = type(p);
|
||||
oxAssert(t != nullptr, "field(const char *name, T *val, std::size_t): Type not found or generated");
|
||||
m_type->fieldList.emplace_back(t, String(name), detail::indirectionLevels_v<T> + 1, subscriptStack, buildTypeId(*t));
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
return ox::Error(1);
|
||||
}
|
||||
@@ -220,7 +220,7 @@ constexpr Error TypeDescWriter::field(StringViewCR name, T const*, std::size_t)
|
||||
auto const lvls = detail::indirectionLevels_v<T> + 1;
|
||||
SubscriptStack subscriptStack{lvls};
|
||||
m_type->fieldList.emplace_back(t, String(name), lvls, subscriptStack, buildTypeId(*t));
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
return ox::Error(1);
|
||||
}
|
||||
@@ -231,7 +231,7 @@ constexpr Error TypeDescWriter::field(StringViewCR name, UnionView<T, force> val
|
||||
const auto t = type(val);
|
||||
oxAssert(t != nullptr, "field(const char *name, T val): Type not found or generated");
|
||||
m_type->fieldList.emplace_back(t, String(name), 0, SubscriptStack{}, ox::String(t->typeName));
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
return ox::Error(1);
|
||||
}
|
||||
|
||||
8
deps/ox/src/ox/model/fieldcounter.hpp
vendored
8
deps/ox/src/ox/model/fieldcounter.hpp
vendored
@@ -31,25 +31,25 @@ class FieldCounter {
|
||||
template<typename U>
|
||||
constexpr ox::Error field(StringViewCR, U) noexcept {
|
||||
++fields;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
constexpr ox::Error field(StringViewCR, U, std::size_t) noexcept {
|
||||
++fields;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename U, typename Handler>
|
||||
constexpr Error field(StringViewCR, Handler) {
|
||||
++fields;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename ...Args>
|
||||
constexpr Error fieldCString(Args&&...) noexcept {
|
||||
++fields;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
static constexpr auto opType() noexcept {
|
||||
|
||||
24
deps/ox/src/ox/model/modelops.hpp
vendored
24
deps/ox/src/ox/model/modelops.hpp
vendored
@@ -56,19 +56,19 @@ class MemberList {
|
||||
template<typename T>
|
||||
constexpr Error field(const char*, T *v) noexcept {
|
||||
vars[m_i++] = static_cast<void*>(v);
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Error field(const char*, T *v, int) noexcept {
|
||||
vars[m_i++] = static_cast<void*>(v);
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename U, bool force = false>
|
||||
constexpr Error field(const char*, UnionView<U, force> u) noexcept {
|
||||
vars[m_i++] = static_cast<void*>(u.get());
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -107,7 +107,7 @@ class Copier {
|
||||
auto &dst = *cbit_cast<FT*>(m_dst->vars[m_i]);
|
||||
dst = src;
|
||||
++m_i;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ class Copier {
|
||||
dst = src;
|
||||
}
|
||||
++m_i;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename U, bool force = false>
|
||||
@@ -128,7 +128,7 @@ class Copier {
|
||||
auto &src = *u.get();
|
||||
dst = src;
|
||||
++m_i;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T = void>
|
||||
@@ -168,7 +168,7 @@ class Mover {
|
||||
dst = std::move(src);
|
||||
src = FT{};
|
||||
++m_i;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ class Mover {
|
||||
src = FT{};
|
||||
}
|
||||
++m_i;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename U, bool force = false>
|
||||
@@ -190,7 +190,7 @@ class Mover {
|
||||
auto &src = *u.get();
|
||||
dst = std::move(src);
|
||||
++m_i;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T = void>
|
||||
@@ -228,7 +228,7 @@ class Equals {
|
||||
const auto &dst = std::bit_cast<FT>(*m_other->vars[m_i]);
|
||||
++m_i;
|
||||
if (dst == src) {
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
} else {
|
||||
this->value = false;
|
||||
return ox::Error(1);
|
||||
@@ -246,7 +246,7 @@ class Equals {
|
||||
}
|
||||
}
|
||||
++m_i;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename U, bool force = false>
|
||||
@@ -255,7 +255,7 @@ class Equals {
|
||||
const auto &src = *u.get();
|
||||
++m_i;
|
||||
if (dst == src) {
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
} else {
|
||||
this->value = false;
|
||||
return ox::Error(1);
|
||||
|
||||
2
deps/ox/src/ox/model/modelvalue.cpp
vendored
2
deps/ox/src/ox/model/modelvalue.cpp
vendored
@@ -18,6 +18,6 @@ static_assert([]() -> ox::Error {
|
||||
}
|
||||
//oxReturnError(v.set<int32_t>(5));
|
||||
return {};
|
||||
}() == ox::Error(0));
|
||||
}() == ox::Error{});
|
||||
|
||||
}
|
||||
|
||||
10
deps/ox/src/ox/model/modelvalue.hpp
vendored
10
deps/ox/src/ox/model/modelvalue.hpp
vendored
@@ -972,7 +972,7 @@ constexpr Error model(auto *h, CommonPtrWith<ModelObject> auto *obj) noexcept {
|
||||
for (auto &f : obj->m_fieldsOrder) {
|
||||
OX_RETURN_ERROR(h->field(f->name.c_str(), &f->value));
|
||||
}
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
constexpr Error model(auto *h, CommonPtrWith<ModelUnion> auto *obj) noexcept {
|
||||
@@ -981,7 +981,7 @@ constexpr Error model(auto *h, CommonPtrWith<ModelUnion> auto *obj) noexcept {
|
||||
for (auto &f : obj->m_fieldsOrder) {
|
||||
OX_RETURN_ERROR(h->field(f->name.c_str(), &f->value));
|
||||
}
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
constexpr ModelValue::ModelValue(const ModelValue &other) noexcept {
|
||||
@@ -1129,7 +1129,7 @@ constexpr Error ModelValue::setType(
|
||||
OX_RETURN_ERROR(m_data.uni->setType(type));
|
||||
}
|
||||
oxAssert(m_type != Type::Undefined, "No type set");
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -1184,7 +1184,7 @@ constexpr Error ModelValue::set(const T &v) noexcept {
|
||||
safeDelete(&value);
|
||||
}
|
||||
value = v;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -1199,7 +1199,7 @@ constexpr Error ModelValue::set(T &&v) noexcept {
|
||||
safeDelete(&value);
|
||||
}
|
||||
value = std::move(v);
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
constexpr ModelValue &ModelValue::operator=(ModelValue &other) noexcept {
|
||||
|
||||
12
deps/ox/src/ox/model/typenamecatcher.hpp
vendored
12
deps/ox/src/ox/model/typenamecatcher.hpp
vendored
@@ -38,17 +38,17 @@ struct TypeNameCatcher {
|
||||
|
||||
template<typename T>
|
||||
constexpr Error field(const char*, T*, std::size_t) noexcept {
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Error field(const char*, T) noexcept {
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename ...Args>
|
||||
constexpr Error fieldCString(Args&&...) noexcept {
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
static constexpr auto opType() noexcept {
|
||||
@@ -77,17 +77,17 @@ struct TypeInfoCatcher {
|
||||
|
||||
template<typename T>
|
||||
constexpr Error field(const char*, T*, std::size_t) noexcept {
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Error field(const char*, T) noexcept {
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Error fieldCString(const char*, T) noexcept {
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
static constexpr auto opType() noexcept {
|
||||
|
||||
4
deps/ox/src/ox/model/walk.hpp
vendored
4
deps/ox/src/ox/model/walk.hpp
vendored
@@ -127,7 +127,7 @@ static constexpr Error parseField(const DescriptorField &field, Reader *rdr, Dat
|
||||
}
|
||||
}
|
||||
walker->popNamePath();
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename Reader, typename FH>
|
||||
@@ -141,7 +141,7 @@ constexpr Error model(Reader *rdr, DataWalker<Reader, FH> *walker) noexcept {
|
||||
for (const auto &field : fields) {
|
||||
OX_RETURN_ERROR(parseField(field, rdr, walker));
|
||||
}
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename Reader, typename Handler>
|
||||
|
||||
8
deps/ox/src/ox/oc/read.cpp
vendored
8
deps/ox/src/ox/oc/read.cpp
vendored
@@ -37,7 +37,7 @@ OrganicClawReader::OrganicClawReader(Json::Value json, int unionIdx) noexcept:
|
||||
}
|
||||
|
||||
Error OrganicClawReader::field(const char *key, bool *val) noexcept {
|
||||
auto err = ox::Error(0);
|
||||
ox::Error err{};
|
||||
if (targetValid()) {
|
||||
const auto &jv = value(key);
|
||||
if (jv.empty()) {
|
||||
@@ -53,7 +53,7 @@ Error OrganicClawReader::field(const char *key, bool *val) noexcept {
|
||||
}
|
||||
|
||||
Error OrganicClawReader::fieldCString(const char *key, char *val, std::size_t buffLen) noexcept {
|
||||
auto err = ox::Error(0);
|
||||
ox::Error err{};
|
||||
const char *begin = nullptr, *end = nullptr;
|
||||
const auto &jv = value(key);
|
||||
if (targetValid()) {
|
||||
@@ -81,7 +81,7 @@ Error OrganicClawReader::fieldCString(const char *key, char *val, std::size_t bu
|
||||
}
|
||||
|
||||
Error OrganicClawReader::fieldCString(const char *key, char **val) noexcept {
|
||||
auto err = ox::Error(0);
|
||||
ox::Error err{};
|
||||
const char *begin = nullptr, *end = nullptr;
|
||||
const auto &jv = value(key);
|
||||
auto &data = *val;
|
||||
@@ -106,7 +106,7 @@ Error OrganicClawReader::fieldCString(const char *key, char **val) noexcept {
|
||||
}
|
||||
|
||||
Error OrganicClawReader::fieldCString(const char *key, char **val, std::size_t buffLen) noexcept {
|
||||
auto err = ox::Error(0);
|
||||
ox::Error err{};
|
||||
const char *begin = nullptr, *end = nullptr;
|
||||
const auto &jv = value(key);
|
||||
if (targetValid()) {
|
||||
|
||||
12
deps/ox/src/ox/oc/read.hpp
vendored
12
deps/ox/src/ox/oc/read.hpp
vendored
@@ -137,7 +137,7 @@ class OrganicClawReader {
|
||||
|
||||
template<typename T>
|
||||
Error OrganicClawReader::field(const char *key, T *val) noexcept {
|
||||
auto err = ox::Error(0);
|
||||
ox::Error err{};
|
||||
try {
|
||||
if constexpr (is_integer_v<T>) {
|
||||
if (targetValid()) {
|
||||
@@ -190,7 +190,7 @@ Error OrganicClawReader::field(const char *key, T *val) noexcept {
|
||||
|
||||
template<typename U, bool force>
|
||||
Error OrganicClawReader::field(const char *key, UnionView<U, force> val) noexcept {
|
||||
auto err = ox::Error(0);
|
||||
ox::Error err{};
|
||||
if (targetValid()) {
|
||||
const auto &jv = value(key);
|
||||
if (jv.empty() || jv.isObject()) {
|
||||
@@ -207,7 +207,7 @@ Error OrganicClawReader::field(const char *key, UnionView<U, force> val) noexcep
|
||||
|
||||
template<std::size_t L>
|
||||
Error OrganicClawReader::field(const char *key, BasicString<L> *val) noexcept {
|
||||
auto err = ox::Error(0);
|
||||
ox::Error err{};
|
||||
if (targetValid()) {
|
||||
const auto &jv = value(key);
|
||||
if (jv.empty()) {
|
||||
@@ -224,7 +224,7 @@ Error OrganicClawReader::field(const char *key, BasicString<L> *val) noexcept {
|
||||
|
||||
template<std::size_t L>
|
||||
Error OrganicClawReader::field(const char *key, IString<L> *val) noexcept {
|
||||
auto err = ox::Error(0);
|
||||
ox::Error err{};
|
||||
if (targetValid()) {
|
||||
const auto &jv = value(key);
|
||||
if (jv.empty()) {
|
||||
@@ -257,7 +257,7 @@ OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
||||
OX_RETURN_ERROR(handler.field("", &val[i]));
|
||||
OX_ALLOW_UNSAFE_BUFFERS_END
|
||||
}
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -274,7 +274,7 @@ Error OrganicClawReader::field(const char *key, HashMap<String, T> *val) noexcep
|
||||
const auto k = keys[i].c_str();
|
||||
OX_RETURN_ERROR(handler.field(k, &val->operator[](k)));
|
||||
}
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
Error readOC(BufferView buff, auto &val) noexcept {
|
||||
|
||||
2
deps/ox/src/ox/oc/write.cpp
vendored
2
deps/ox/src/ox/oc/write.cpp
vendored
@@ -23,7 +23,7 @@ Error OrganicClawWriter::fieldCString(const char *key, const char *const*val, in
|
||||
value(key) = *val;
|
||||
}
|
||||
++m_fieldIt;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
Error OrganicClawWriter::fieldCString(const char *key, const char *const*val) noexcept {
|
||||
|
||||
24
deps/ox/src/ox/oc/write.hpp
vendored
24
deps/ox/src/ox/oc/write.hpp
vendored
@@ -46,7 +46,7 @@ class OrganicClawWriter {
|
||||
value(key) = *val;
|
||||
}
|
||||
++m_fieldIt;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
Error field(const char *key, const int16_t *val) noexcept {
|
||||
@@ -54,7 +54,7 @@ class OrganicClawWriter {
|
||||
value(key) = *val;
|
||||
}
|
||||
++m_fieldIt;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
Error field(const char *key, const int32_t *val) noexcept {
|
||||
@@ -62,7 +62,7 @@ class OrganicClawWriter {
|
||||
value(key) = *val;
|
||||
}
|
||||
++m_fieldIt;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
Error field(const char *key, const int64_t *val) noexcept {
|
||||
@@ -70,7 +70,7 @@ class OrganicClawWriter {
|
||||
value(key) = *val;
|
||||
}
|
||||
++m_fieldIt;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ class OrganicClawWriter {
|
||||
value(key) = *val;
|
||||
}
|
||||
++m_fieldIt;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
Error field(const char *key, const uint16_t *val) noexcept {
|
||||
@@ -87,7 +87,7 @@ class OrganicClawWriter {
|
||||
value(key) = *val;
|
||||
}
|
||||
++m_fieldIt;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
Error field(const char *key, const uint32_t *val) noexcept {
|
||||
@@ -95,7 +95,7 @@ class OrganicClawWriter {
|
||||
value(key) = *val;
|
||||
}
|
||||
++m_fieldIt;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
Error field(const char *key, const uint64_t *val) noexcept {
|
||||
@@ -103,7 +103,7 @@ class OrganicClawWriter {
|
||||
value(key) = *val;
|
||||
}
|
||||
++m_fieldIt;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
Error field(char const*key, bool const*val) noexcept {
|
||||
@@ -151,7 +151,7 @@ class OrganicClawWriter {
|
||||
value(key) = val->c_str();
|
||||
}
|
||||
++m_fieldIt;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
Error fieldCString(const char*, const char *const*val, int len) noexcept;
|
||||
@@ -211,7 +211,7 @@ OX_ALLOW_UNSAFE_BUFFERS_END
|
||||
value(key) = w.m_json;
|
||||
}
|
||||
++m_fieldIt;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -237,7 +237,7 @@ Error OrganicClawWriter::field(const char *key, const T *val) noexcept {
|
||||
}
|
||||
}
|
||||
++m_fieldIt;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename U, bool force>
|
||||
@@ -251,7 +251,7 @@ Error OrganicClawWriter::field(const char *key, UnionView<U, force> val) noexcep
|
||||
}
|
||||
}
|
||||
++m_fieldIt;
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<ox::Buffer> writeOC(const auto &val) noexcept {
|
||||
|
||||
2
deps/ox/src/ox/std/string.hpp
vendored
2
deps/ox/src/ox/std/string.hpp
vendored
@@ -182,7 +182,7 @@ class BasicString {
|
||||
// make sure last element is a null terminator
|
||||
m_buff[currentLen + strLen] = 0;
|
||||
// this can't fail, but it returns an Error to match BString::append
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
constexpr Error append(ox::StringView sv) noexcept {
|
||||
|
||||
5
deps/ox/src/ox/std/stringview.hpp
vendored
5
deps/ox/src/ox/std/stringview.hpp
vendored
@@ -104,13 +104,16 @@ constexpr ox::Result<int> strToInt(StringViewCR str) noexcept {
|
||||
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
||||
int total = 0;
|
||||
int multiplier = 1;
|
||||
if (str.len() == 0) [[unlikely]] {
|
||||
return Error{1, "Empty string passed to strToInt"};
|
||||
}
|
||||
for (auto i = static_cast<int64_t>(str.len()) - 1; i != -1; --i) {
|
||||
auto s = static_cast<std::size_t>(i);
|
||||
if (str[s] >= '0' && str[s] <= '9') {
|
||||
total += (str[s] - '0') * multiplier;
|
||||
multiplier *= 10;
|
||||
} else {
|
||||
return ox::Error(1);
|
||||
return ox::Error{1};
|
||||
}
|
||||
}
|
||||
return total;
|
||||
|
||||
6
deps/ox/src/ox/std/vector.hpp
vendored
6
deps/ox/src/ox/std/vector.hpp
vendored
@@ -311,7 +311,7 @@ class Vector: detail::VectorAllocator<T, Allocator, SmallVectorSize> {
|
||||
*/
|
||||
constexpr Error unordered_erase(std::size_t pos) noexcept(useNoexcept);
|
||||
|
||||
constexpr Error remove(T const &val);
|
||||
constexpr Error remove(MaybeView_t<T> const &val);
|
||||
|
||||
constexpr void reserve(std::size_t cap) noexcept(useNoexcept);
|
||||
|
||||
@@ -658,11 +658,11 @@ constexpr Error Vector<T, SmallVectorSize, Allocator>::unordered_erase(std::size
|
||||
--m_size;
|
||||
m_items[pos] = std::move(m_items[m_size]);
|
||||
m_items[m_size].~T();
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T, std::size_t SmallVectorSize, typename Allocator>
|
||||
constexpr ox::Error Vector<T, SmallVectorSize, Allocator>::remove(T const &val) {
|
||||
constexpr ox::Error Vector<T, SmallVectorSize, Allocator>::remove(MaybeView_t<T> const &val) {
|
||||
for (size_t i{}; auto const &v : *this) {
|
||||
if (v == val) {
|
||||
return erase(i).error;
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
# d2025.06.0
|
||||
|
||||
* Add ability to remember recent projects in config
|
||||
* Add navigation support (back and forward)
|
||||
* Fix file deletion to close file even if not active
|
||||
* Fix file copy to work when creating a copy with the name of a previously
|
||||
deleted file
|
||||
* Fix crash that could occur after switching projects
|
||||
* Make file picker popup accept on double click of a file
|
||||
* TileSheetEditor: Fix copy/cut/paste enablement when there is no selection
|
||||
* TileSheetEditor: Fix manual redo of draw actions, fix drawing to pixel 0, 0
|
||||
as first action
|
||||
* TileSheetEditor: Fix draw command to work on same pixel after switching
|
||||
subsheets
|
||||
* PaletteEditor: Add RGB key shortcuts for focusing color channels
|
||||
* PaletteEditor: Add color preview to color editor
|
||||
|
||||
# d2025.05.1
|
||||
|
||||
|
||||
@@ -142,9 +142,9 @@ constexpr Color16 color16(int r, int g, int b, int a = 0) noexcept {
|
||||
[[nodiscard]]
|
||||
constexpr Color16 color16(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 0) noexcept {
|
||||
return static_cast<Color16>(ox::min<uint8_t>(r, 31))
|
||||
| static_cast<Color16>(ox::min<uint8_t>(g, 31) << 5)
|
||||
| static_cast<Color16>(ox::min<uint8_t>(b, 31) << 10)
|
||||
| static_cast<Color16>(a << 15);
|
||||
| static_cast<Color16>(ox::min<uint8_t>(g, 31) << 5)
|
||||
| static_cast<Color16>(ox::min<uint8_t>(b, 31) << 10)
|
||||
| static_cast<Color16>(a << 15);
|
||||
}
|
||||
|
||||
static_assert(color16(0, 31, 0) == 992);
|
||||
|
||||
@@ -350,7 +350,7 @@ void panic(const char *file, int line, const char *panicMsg, ox::Error const&err
|
||||
const auto heapEnd = reinterpret_cast<char*>(MEM_EWRAM_BEGIN + heapSz);
|
||||
ox::heapmgr::initHeap(heapBegin, heapEnd);
|
||||
OX_ALLOW_UNSAFE_BUFFERS_END
|
||||
auto tctx = turbine::init(keel::loadRomFs("").unwrap(), "Nostalgia").unwrap();
|
||||
auto tctx = turbine::init(keel::loadRomFs("").unwrap(), "Nostalgia").unwrap();
|
||||
auto ctx = init(*tctx).unwrap();
|
||||
std::ignore = initGfx();
|
||||
std::ignore = initConsole(*ctx);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
namespace nostalgia::gfx {
|
||||
|
||||
RemovePageCommand::RemovePageCommand(Palette &pal, size_t idx) noexcept:
|
||||
RemovePageCommand::RemovePageCommand(Palette &pal, size_t const idx) noexcept:
|
||||
m_pal(pal),
|
||||
m_idx(idx) {}
|
||||
|
||||
|
||||
@@ -9,13 +9,13 @@ namespace nostalgia::gfx {
|
||||
|
||||
UpdateColorCommand::UpdateColorCommand(
|
||||
Palette &pal,
|
||||
size_t page,
|
||||
size_t idx,
|
||||
Color16 newColor):
|
||||
m_pal(pal),
|
||||
m_page(page),
|
||||
m_idx(idx),
|
||||
m_altColor(newColor) {
|
||||
size_t const page,
|
||||
size_t const idx,
|
||||
Color16 const newColor):
|
||||
m_pal{pal},
|
||||
m_page{page},
|
||||
m_idx{idx},
|
||||
m_altColor{newColor} {
|
||||
if (color(m_pal, m_page, m_idx) == newColor) {
|
||||
throw studio::NoChangesException();
|
||||
}
|
||||
|
||||
@@ -302,29 +302,43 @@ void PaletteEditorImGui::drawColorEditor() noexcept {
|
||||
int const a = alpha16(c);
|
||||
auto const newName = ig::InputText<50>(
|
||||
"Name", m_pal.colorNames[m_selectedColorRow]);
|
||||
bool inputFocused = ImGui::IsItemFocused();
|
||||
auto const nameInputFocused = ImGui::IsItemFocused();
|
||||
ImGui::Separator();
|
||||
colorInput("Red", r, inputFocused, FocusCmd::Red);
|
||||
colorInput("Green", g, inputFocused, FocusCmd::Green);
|
||||
colorInput("Blue", b, inputFocused, FocusCmd::Blue);
|
||||
bool colorInFocused{};
|
||||
colorInput("Red", r, colorInFocused, FocusCmd::Red);
|
||||
colorInput("Green", g, colorInFocused, FocusCmd::Green);
|
||||
colorInput("Blue", b, colorInFocused, FocusCmd::Blue);
|
||||
// color preview
|
||||
{
|
||||
ImGui::PushStyleColor(
|
||||
ImGuiCol_ChildBg,
|
||||
color32(color16(r, g, b)) | 0xff'00'00'00);
|
||||
ImGui::BeginChild("ColorPreview", {0, 25});
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
if (ig::PushButton("Apply to all pages", {-1, ig::BtnSz.y})) {
|
||||
std::ignore = pushCommand<ApplyColorAllPagesCommand>(
|
||||
m_pal, m_page, m_selectedColorRow);
|
||||
}
|
||||
if (ig::mainWinHasFocus() && !inputFocused && !ImGui::IsKeyDown(ImGuiKey_ModCtrl)) {
|
||||
if (ig::mainWinHasFocus() && !ImGui::IsKeyDown(ImGuiKey_ModCtrl)) {
|
||||
if (!ImGui::IsKeyDown(ImGuiKey_ModAlt)) {
|
||||
numShortcuts(m_selectedColorRow, largestPage(m_pal));
|
||||
if (!nameInputFocused && !colorInFocused) {
|
||||
numShortcuts(m_selectedColorRow, largestPage(m_pal));
|
||||
}
|
||||
} else {
|
||||
numShortcuts(m_page, m_pal.pages.size());
|
||||
}
|
||||
if (ImGui::IsKeyDown(ImGuiKey_R)) {
|
||||
m_focusCmd = FocusCmd::Red;
|
||||
}
|
||||
if (ImGui::IsKeyDown(ImGuiKey_G)) {
|
||||
m_focusCmd = FocusCmd::Green;
|
||||
}
|
||||
if (ImGui::IsKeyDown(ImGuiKey_B)) {
|
||||
m_focusCmd = FocusCmd::Blue;
|
||||
if (!nameInputFocused) {
|
||||
if (ImGui::IsKeyDown(ImGuiKey_R)) {
|
||||
m_focusCmd = FocusCmd::Red;
|
||||
}
|
||||
if (ImGui::IsKeyDown(ImGuiKey_G)) {
|
||||
m_focusCmd = FocusCmd::Green;
|
||||
}
|
||||
if (ImGui::IsKeyDown(ImGuiKey_B)) {
|
||||
m_focusCmd = FocusCmd::Blue;
|
||||
}
|
||||
}
|
||||
}
|
||||
auto const newColor = color16(r, g, b, a);
|
||||
|
||||
@@ -13,7 +13,7 @@ AddSubSheetCommand::AddSubSheetCommand(
|
||||
auto &parent = getSubSheet(m_img, m_parentIdx);
|
||||
if (!parent.subsheets.empty()) {
|
||||
auto idx = m_parentIdx;
|
||||
idx.emplace_back(parent.subsheets.size());
|
||||
idx.emplace_back(static_cast<uint32_t>(parent.subsheets.size()));
|
||||
m_addedSheets.push_back(idx);
|
||||
} else {
|
||||
auto idx = m_parentIdx;
|
||||
|
||||
@@ -13,20 +13,19 @@ DeleteTilesCommand::DeleteTilesCommand(
|
||||
TileSheet::SubSheetIdx idx,
|
||||
std::size_t const tileIdx,
|
||||
std::size_t const tileCnt) noexcept:
|
||||
m_img(img),
|
||||
m_idx(std::move(idx)) {
|
||||
constexpr unsigned bytesPerTile = PixelsPerTile;
|
||||
m_deletePos = tileIdx * bytesPerTile;
|
||||
m_deleteSz = tileCnt * bytesPerTile;
|
||||
m_deletedPixels.resize(m_deleteSz);
|
||||
// copy pixels to be erased
|
||||
{
|
||||
auto &s = getSubSheet(m_img, m_idx);
|
||||
auto dst = m_deletedPixels.begin();
|
||||
auto src = s.pixels.begin() + m_deletePos;
|
||||
ox::copy_n(src, m_deleteSz, dst);
|
||||
}
|
||||
}
|
||||
m_img{img},
|
||||
m_idx{std::move(idx)},
|
||||
m_deletePos{tileIdx * PixelsPerTile},
|
||||
m_deleteSz{tileCnt * PixelsPerTile},
|
||||
m_deletedPixels{[this] {
|
||||
ox::Vector<uint8_t> deletedPixels(m_deleteSz);
|
||||
// copy pixels to be erased
|
||||
auto const &s = getSubSheet(m_img, m_idx);
|
||||
auto const dst = deletedPixels.begin();
|
||||
auto const src = s.pixels.begin() + m_deletePos;
|
||||
ox::copy_n(src, m_deleteSz, dst);
|
||||
return deletedPixels;
|
||||
}()} {}
|
||||
|
||||
ox::Error DeleteTilesCommand::redo() noexcept {
|
||||
auto &s = getSubSheet(m_img, m_idx);
|
||||
|
||||
@@ -11,10 +11,10 @@ namespace nostalgia::gfx {
|
||||
class DeleteTilesCommand: public TileSheetCommand {
|
||||
private:
|
||||
TileSheet &m_img;
|
||||
TileSheet::SubSheetIdx m_idx;
|
||||
std::size_t m_deletePos = 0;
|
||||
std::size_t m_deleteSz = 0;
|
||||
ox::Vector<uint8_t> m_deletedPixels = {};
|
||||
TileSheet::SubSheetIdx const m_idx;
|
||||
std::size_t const m_deletePos = 0;
|
||||
std::size_t const m_deleteSz = 0;
|
||||
ox::Vector<uint8_t> const m_deletedPixels;
|
||||
|
||||
public:
|
||||
DeleteTilesCommand(
|
||||
|
||||
@@ -62,11 +62,11 @@ constexpr void iterateLine(ox::Point const &a, ox::Point const &b, auto const &f
|
||||
DrawCommand::DrawCommand(
|
||||
TileSheet &img,
|
||||
TileSheet::SubSheetIdx subSheetIdx,
|
||||
std::size_t idx,
|
||||
std::size_t const idx,
|
||||
int const palIdx) noexcept:
|
||||
m_img(img),
|
||||
m_subSheetIdx(std::move(subSheetIdx)),
|
||||
m_palIdx(palIdx) {
|
||||
m_img{img},
|
||||
m_subSheetIdx{std::move(subSheetIdx)},
|
||||
m_palIdx{palIdx} {
|
||||
auto &subsheet = getSubSheet(m_img, m_subSheetIdx);
|
||||
m_changes.emplace_back(static_cast<uint32_t>(idx), getPixel(subsheet, idx));
|
||||
}
|
||||
|
||||
@@ -12,19 +12,19 @@ InsertTilesCommand::InsertTilesCommand(
|
||||
std::size_t const tileIdx,
|
||||
std::size_t const tileCnt) noexcept:
|
||||
m_img{img},
|
||||
m_idx{std::move(idx)} {
|
||||
m_insertPos = tileIdx * PixelsPerTile;
|
||||
m_insertCnt = tileCnt * PixelsPerTile;
|
||||
m_deletedPixels.resize(m_insertCnt);
|
||||
// copy pixels to be erased
|
||||
{
|
||||
auto &s = getSubSheet(m_img, m_idx);
|
||||
auto &p = s.pixels;
|
||||
auto const dst = m_deletedPixels.begin();
|
||||
auto const src = p.begin() + p.size() - m_insertCnt;
|
||||
ox::copy_n(src, m_insertCnt, dst);
|
||||
}
|
||||
}
|
||||
m_idx{std::move(idx)},
|
||||
m_insertPos{tileIdx * PixelsPerTile},
|
||||
m_insertCnt{tileCnt * PixelsPerTile},
|
||||
m_insertedPixels{[this] {
|
||||
ox::Vector<uint8_t> insertedPixels(m_insertCnt);
|
||||
// copy pixels to be erased
|
||||
auto &s = getSubSheet(m_img, m_idx);
|
||||
auto &p = s.pixels;
|
||||
auto const dst = insertedPixels.begin();
|
||||
auto const src = p.begin() + p.size() - m_insertCnt;
|
||||
ox::copy_n(src, m_insertCnt, dst);
|
||||
return insertedPixels;
|
||||
}()} {}
|
||||
|
||||
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
||||
|
||||
@@ -52,7 +52,7 @@ ox::Error InsertTilesCommand::undo() noexcept {
|
||||
auto const src = &p[srcIdx];
|
||||
ox::memmove(dst1, src, sz);
|
||||
}
|
||||
ox::memcpy(dst2, m_deletedPixels.data(), m_deletedPixels.size());
|
||||
ox::memcpy(dst2, m_insertedPixels.data(), m_insertedPixels.size());
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -11,10 +11,10 @@ namespace nostalgia::gfx {
|
||||
class InsertTilesCommand: public TileSheetCommand {
|
||||
private:
|
||||
TileSheet &m_img;
|
||||
TileSheet::SubSheetIdx m_idx;
|
||||
std::size_t m_insertPos = 0;
|
||||
std::size_t m_insertCnt = 0;
|
||||
ox::Vector<uint8_t> m_deletedPixels = {};
|
||||
TileSheet::SubSheetIdx const m_idx;
|
||||
std::size_t const m_insertPos{};
|
||||
std::size_t const m_insertCnt{};
|
||||
ox::Vector<uint8_t> const m_insertedPixels;
|
||||
|
||||
public:
|
||||
InsertTilesCommand(
|
||||
|
||||
@@ -15,7 +15,6 @@ UpdateSubSheetCommand::UpdateSubSheetCommand(
|
||||
m_img{img},
|
||||
m_idx{std::move(idx)},
|
||||
m_sheet{getSubSheet(m_img, m_idx)} {
|
||||
m_sheet = getSubSheet(m_img, m_idx);
|
||||
m_sheet.name = std::move(name);
|
||||
OX_THROW_ERROR(resizeSubsheet(m_sheet, {cols, rows}));
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace nostalgia::gfx {
|
||||
class UpdateSubSheetCommand: public TileSheetCommand {
|
||||
private:
|
||||
TileSheet &m_img;
|
||||
TileSheet::SubSheetIdx m_idx;
|
||||
TileSheet::SubSheetIdx const m_idx;
|
||||
TileSheet::SubSheet m_sheet;
|
||||
|
||||
public:
|
||||
|
||||
@@ -36,7 +36,7 @@ OX_MODEL_BEGIN(TileSheetEditorConfig)
|
||||
OX_MODEL_END()
|
||||
|
||||
static ox::Vector<uint32_t> normalizePixelSizes(
|
||||
ox::Vector<uint8_t> const&inPixels) noexcept {
|
||||
ox::Vector<uint8_t> const &inPixels) noexcept {
|
||||
ox::Vector<uint32_t> outPixels;
|
||||
outPixels.reserve(inPixels.size());
|
||||
outPixels.resize(inPixels.size());
|
||||
@@ -47,7 +47,7 @@ static ox::Vector<uint32_t> normalizePixelSizes(
|
||||
}
|
||||
|
||||
static ox::Vector<uint32_t> normalizePixelArrangement(
|
||||
ox::Vector<uint32_t> const&inPixels,
|
||||
ox::Vector<uint32_t> const &inPixels,
|
||||
int const cols,
|
||||
int const scale) {
|
||||
auto const scalePt = ox::Point{scale, scale};
|
||||
@@ -67,10 +67,10 @@ static ox::Vector<uint32_t> normalizePixelArrangement(
|
||||
}
|
||||
|
||||
static ox::Error toPngFile(
|
||||
ox::CStringView const&path,
|
||||
ox::CStringViewCR path,
|
||||
ox::Vector<uint32_t> &&pixels,
|
||||
Palette const&pal,
|
||||
size_t page,
|
||||
Palette const &pal,
|
||||
size_t const page,
|
||||
unsigned const width,
|
||||
unsigned const height) noexcept {
|
||||
for (auto &c : pixels) {
|
||||
@@ -88,7 +88,7 @@ static ox::Error toPngFile(
|
||||
}
|
||||
|
||||
TileSheetEditorImGui::TileSheetEditorImGui(studio::Context &sctx, ox::StringParam path):
|
||||
Editor(sctx, std::move(path)),
|
||||
Editor{sctx, std::move(path)},
|
||||
m_sctx{sctx},
|
||||
m_tctx{m_sctx.tctx},
|
||||
m_palPicker{"Palette Chooser", keelCtx(sctx), FileExt_npal},
|
||||
@@ -98,8 +98,11 @@ TileSheetEditorImGui::TileSheetEditorImGui(studio::Context &sctx, ox::StringPara
|
||||
m_subsheetEditor.inputSubmitted.connect(this, &TileSheetEditorImGui::updateActiveSubsheet);
|
||||
m_exportMenu.inputSubmitted.connect(this, &TileSheetEditorImGui::exportSubsheetToPng);
|
||||
// load config
|
||||
auto const&config = studio::readConfig<TileSheetEditorConfig>(
|
||||
keelCtx(m_sctx), itemPath());
|
||||
auto &kctx = keelCtx(m_sctx);
|
||||
auto const ip = itemPath();
|
||||
oxLogError(studio::headerizeConfigFile<TileSheetEditorConfig>(kctx, ip));
|
||||
auto const &config =
|
||||
studio::readConfig<TileSheetEditorConfig>(kctx, ip);
|
||||
if (config.ok()) {
|
||||
m_model.setActiveSubsheet(validateSubSheetIdx(m_model.img(), config.value.activeSubsheet));
|
||||
}
|
||||
@@ -189,10 +192,13 @@ void TileSheetEditorImGui::keyStateChanged(turbine::Key const key, bool const do
|
||||
}
|
||||
|
||||
void TileSheetEditorImGui::draw(studio::Context&) noexcept {
|
||||
setCopyEnabled(m_model.hasSelection());
|
||||
setCutEnabled(m_model.hasSelection());
|
||||
setPasteEnabled(m_model.hasSelection());
|
||||
if (ig::mainWinHasFocus() && m_tool == TileSheetTool::Select) {
|
||||
if (ImGui::IsKeyDown(ImGuiKey_ModCtrl) && !m_palPathFocused) {
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_A)) {
|
||||
auto const&img = m_model.activeSubSheet();
|
||||
auto const &img = m_model.activeSubSheet();
|
||||
m_model.setSelection({{}, {img.columns * TileWidth - 1, img.rows * TileHeight - 1}});
|
||||
} else if (ImGui::IsKeyPressed(ImGuiKey_G)) {
|
||||
m_model.clearSelection();
|
||||
@@ -271,14 +277,14 @@ void TileSheetEditorImGui::draw(studio::Context&) noexcept {
|
||||
auto constexpr btnSize = ImVec2{btnHeight, btnHeight};
|
||||
if (ig::PushButton("+", btnSize)) {
|
||||
auto insertOnIdx = m_model.activeSubSheetIdx();
|
||||
auto const&parent = m_model.activeSubSheet();
|
||||
auto const &parent = m_model.activeSubSheet();
|
||||
m_model.addSubsheet(insertOnIdx);
|
||||
insertOnIdx.emplace_back(parent.subsheets.size() - 1);
|
||||
insertOnIdx.emplace_back(static_cast<uint32_t>(parent.subsheets.size() - 1));
|
||||
setActiveSubsheet(insertOnIdx);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ig::PushButton("-", btnSize)) {
|
||||
auto const&activeSubsheetIdx = m_model.activeSubSheetIdx();
|
||||
auto const &activeSubsheetIdx = m_model.activeSubSheetIdx();
|
||||
if (!activeSubsheetIdx.empty()) {
|
||||
m_model.rmSubsheet(activeSubsheetIdx);
|
||||
}
|
||||
@@ -378,7 +384,7 @@ void TileSheetEditorImGui::drawSubsheetSelector(
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
ox::Vec2 TileSheetEditorImGui::clickPos(ImVec2 const&winPos, ox::Vec2 clickPos) noexcept {
|
||||
ox::Vec2 TileSheetEditorImGui::clickPos(ImVec2 const &winPos, ox::Vec2 clickPos) noexcept {
|
||||
clickPos.x -= winPos.x + 10;
|
||||
clickPos.y -= winPos.y + 10;
|
||||
return clickPos;
|
||||
@@ -400,7 +406,7 @@ void TileSheetEditorImGui::navigateTo(ox::StringViewCR arg) noexcept {
|
||||
}
|
||||
|
||||
void TileSheetEditorImGui::showSubsheetEditor() noexcept {
|
||||
auto const&sheet = m_model.activeSubSheet();
|
||||
auto const &sheet = m_model.activeSubSheet();
|
||||
if (!sheet.subsheets.empty()) {
|
||||
m_subsheetEditor.show(sheet.name, -1, -1);
|
||||
} else {
|
||||
@@ -411,8 +417,8 @@ void TileSheetEditorImGui::showSubsheetEditor() noexcept {
|
||||
ox::Error TileSheetEditorImGui::exportSubsheetToPng(int const scale) const noexcept {
|
||||
OX_REQUIRE(path, studio::saveFile({{"PNG", "png"}}));
|
||||
// subsheet to png
|
||||
auto const&s = m_model.activeSubSheet();
|
||||
auto const&pal = m_model.pal();
|
||||
auto const &s = m_model.activeSubSheet();
|
||||
auto const &pal = m_model.pal();
|
||||
auto const width = s.columns * TileWidth;
|
||||
auto const height = s.rows * TileHeight;
|
||||
auto pixels = normalizePixelSizes(s.pixels);
|
||||
@@ -430,7 +436,7 @@ ox::Error TileSheetEditorImGui::exportSubsheetToPng(int const scale) const noexc
|
||||
return err;
|
||||
}
|
||||
|
||||
void TileSheetEditorImGui::drawTileSheet(ox::Vec2 const&fbSize) noexcept {
|
||||
void TileSheetEditorImGui::drawTileSheet(ox::Vec2 const &fbSize) noexcept {
|
||||
auto const winPos = ImGui::GetWindowPos();
|
||||
auto const fbSizei = ox::Size{static_cast<int>(fbSize.x), static_cast<int>(fbSize.y)};
|
||||
if (m_framebuffer.width != fbSizei.width || m_framebuffer.height != fbSizei.height) {
|
||||
@@ -451,7 +457,7 @@ void TileSheetEditorImGui::drawTileSheet(ox::Vec2 const&fbSize) noexcept {
|
||||
{0, 1},
|
||||
{1, 0});
|
||||
// handle input, this must come after drawing
|
||||
auto const&io = ImGui::GetIO();
|
||||
auto const &io = ImGui::GetIO();
|
||||
auto const mousePos = ox::Vec2{ImGui::GetMousePos()};
|
||||
if (ImGui::IsItemHovered()) {
|
||||
auto const wheel = io.MouseWheel;
|
||||
@@ -556,9 +562,9 @@ void TileSheetEditorImGui::drawPaletteMenu() noexcept {
|
||||
ImGui::TableSetupColumn("Color16", 0, 3);
|
||||
ImGui::TableHeadersRow();
|
||||
{
|
||||
auto const&pal = m_model.pal();
|
||||
auto const &pal = m_model.pal();
|
||||
if (pal.pages.size() > m_model.palettePage()) {
|
||||
for (auto i = 0u; auto const&c: pal.pages[m_model.palettePage()].colors) {
|
||||
for (auto i = 0u; auto const &c: pal.pages[m_model.palettePage()].colors) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::PushID(static_cast<int>(i));
|
||||
// Column: color idx
|
||||
@@ -580,7 +586,7 @@ void TileSheetEditorImGui::drawPaletteMenu() noexcept {
|
||||
auto const ic = ImGui::GetColorU32({redf(c), greenf(c), bluef(c), 1});
|
||||
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ic);
|
||||
ImGui::TableNextColumn();
|
||||
auto const&name = i < pal.colorNames.size() ? pal.colorNames[i].c_str() : "";
|
||||
auto const &name = i < pal.colorNames.size() ? pal.colorNames[i].c_str() : "";
|
||||
ImGui::Text("%s", name);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("(%02d, %02d, %02d)", red16(c), green16(c), blue16(c));
|
||||
@@ -594,7 +600,7 @@ void TileSheetEditorImGui::drawPaletteMenu() noexcept {
|
||||
}
|
||||
|
||||
ox::Error TileSheetEditorImGui::updateActiveSubsheet(
|
||||
ox::StringView const&name, int const cols, int const rows) noexcept {
|
||||
ox::StringViewCR name, int const cols, int const rows) noexcept {
|
||||
return m_model.updateSubsheet(m_model.activeSubSheetIdx(), name, cols, rows);
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ class TileSheetEditorImGui: public studio::Editor {
|
||||
void drawSubsheetSelector(TileSheet::SubSheet&, TileSheet::SubSheetIdx &path);
|
||||
|
||||
[[nodiscard]]
|
||||
static ox::Vec2 clickPos(ImVec2 const&winPos, ox::Vec2 clickPos) noexcept;
|
||||
static ox::Vec2 clickPos(ImVec2 const &winPos, ox::Vec2 clickPos) noexcept;
|
||||
|
||||
protected:
|
||||
ox::Error saveItem() noexcept override;
|
||||
@@ -94,11 +94,11 @@ class TileSheetEditorImGui: public studio::Editor {
|
||||
|
||||
ox::Error exportSubsheetToPng(int scale) const noexcept;
|
||||
|
||||
void drawTileSheet(ox::Vec2 const&fbSize) noexcept;
|
||||
void drawTileSheet(ox::Vec2 const &fbSize) noexcept;
|
||||
|
||||
void drawPaletteMenu() noexcept;
|
||||
|
||||
ox::Error updateActiveSubsheet(ox::StringView const&name, int cols, int rows) noexcept;
|
||||
ox::Error updateActiveSubsheet(ox::StringViewCR name, int cols, int rows) noexcept;
|
||||
|
||||
void setActiveSubsheet(TileSheet::SubSheetIdx path) noexcept;
|
||||
|
||||
|
||||
@@ -84,9 +84,9 @@ void TileSheetEditorModel::cut() {
|
||||
auto const pt1 = m_selection->a;
|
||||
auto const pt2 = ox::Point{s.columns * TileWidth, s.rows * TileHeight};
|
||||
turbine::setClipboardObject(m_tctx, std::move(cb));
|
||||
if (auto const cmd = ox::makeCatch<CutPasteCommand>(
|
||||
if (auto cmd = ox::make_unique_catch<CutPasteCommand>(
|
||||
CommandId::Cut, m_img, m_activeSubsSheetIdx, pt1, pt2, blankCb); cmd.ok()) {
|
||||
std::ignore = pushCommand(cmd.value);
|
||||
std::ignore = pushCommand(std::move(cmd.value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,9 +125,9 @@ void TileSheetEditorModel::paste() {
|
||||
auto const&s = activeSubSheet();
|
||||
auto const pt1 = m_selection->a;
|
||||
auto const pt2 = ox::Point{s.columns * TileWidth, s.rows * TileHeight};
|
||||
if (auto const cmd = ox::makeCatch<CutPasteCommand>(
|
||||
CommandId::Paste, m_img, m_activeSubsSheetIdx, pt1, pt2, *cb); cmd.ok()) {
|
||||
std::ignore = pushCommand(cmd.value);
|
||||
if (auto cmd = ox::make_unique_catch<CutPasteCommand>(
|
||||
CommandId::Paste, m_img, m_activeSubsSheetIdx, pt1, pt2, *cb); cmd.value) {
|
||||
std::ignore = pushCommand(std::move(cmd.value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ ox::String const &TileSheetEditorModel::palPath() const & noexcept {
|
||||
|
||||
ox::Error TileSheetEditorModel::setPalette(ox::StringViewCR path) noexcept {
|
||||
OX_REQUIRE(uuid, keelCtx(m_tctx).pathToUuid.at(path));
|
||||
std::ignore = pushCommand(ox::make<PaletteChangeCommand>(
|
||||
std::ignore = pushCommand(ox::make_unique<PaletteChangeCommand>(
|
||||
activeSubSheetIdx(), m_img, uuid->toString()));
|
||||
return {};
|
||||
}
|
||||
@@ -169,7 +169,7 @@ void TileSheetEditorModel::drawCommand(ox::Point const &pt, std::size_t const pa
|
||||
if (m_ongoingDrawCommand) {
|
||||
m_updated = m_updated || m_ongoingDrawCommand->append(idx);
|
||||
} else if (activeSubSheet.pixels[idx] != palIdx) {
|
||||
std::ignore = pushCommand(ox::make<DrawCommand>(
|
||||
std::ignore = pushCommand(ox::make_unique<DrawCommand>(
|
||||
m_img, m_activeSubsSheetIdx, idx, static_cast<int>(palIdx)));
|
||||
}
|
||||
}
|
||||
@@ -188,7 +188,7 @@ void TileSheetEditorModel::drawLineCommand(ox::Point const &pt, std::size_t cons
|
||||
m_ongoingDrawCommand->lineUpdate(m_lineStartPt, pt);
|
||||
m_updated = true;
|
||||
} else {
|
||||
std::ignore = pushCommand(ox::make<DrawCommand>(
|
||||
std::ignore = pushCommand(ox::make_unique<DrawCommand>(
|
||||
m_img, m_activeSubsSheetIdx, idx, static_cast<int>(palIdx)));
|
||||
m_lineStartPt = pt;
|
||||
}
|
||||
@@ -198,41 +198,43 @@ void TileSheetEditorModel::endDrawCommand() noexcept {
|
||||
if (m_ongoingDrawCommand) {
|
||||
m_ongoingDrawCommand->finish();
|
||||
m_ongoingDrawCommand = nullptr;
|
||||
m_lastDrawUpdatePt = {-1, -1};
|
||||
}
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::addSubsheet(TileSheet::SubSheetIdx const&parentIdx) noexcept {
|
||||
std::ignore = pushCommand(ox::make<AddSubSheetCommand>(m_img, parentIdx));
|
||||
void TileSheetEditorModel::addSubsheet(TileSheet::SubSheetIdx const &parentIdx) noexcept {
|
||||
std::ignore = pushCommand(ox::make_unique<AddSubSheetCommand>(m_img, parentIdx));
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::rmSubsheet(TileSheet::SubSheetIdx const&idx) noexcept {
|
||||
std::ignore = pushCommand(ox::make<RmSubSheetCommand>(m_img, idx));
|
||||
void TileSheetEditorModel::rmSubsheet(TileSheet::SubSheetIdx const &idx) noexcept {
|
||||
std::ignore = pushCommand(ox::make_unique<RmSubSheetCommand>(m_img, idx));
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::insertTiles(
|
||||
TileSheet::SubSheetIdx const&idx, std::size_t const tileIdx, std::size_t const tileCnt) noexcept {
|
||||
std::ignore = pushCommand(ox::make<InsertTilesCommand>(m_img, idx, tileIdx, tileCnt));
|
||||
TileSheet::SubSheetIdx const &idx, std::size_t const tileIdx, std::size_t const tileCnt) noexcept {
|
||||
std::ignore = pushCommand(ox::make_unique<InsertTilesCommand>(m_img, idx, tileIdx, tileCnt));
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::deleteTiles(
|
||||
TileSheet::SubSheetIdx const&idx, std::size_t const tileIdx, std::size_t const tileCnt) noexcept {
|
||||
std::ignore = pushCommand(ox::make<DeleteTilesCommand>(m_img, idx, tileIdx, tileCnt));
|
||||
TileSheet::SubSheetIdx const &idx, std::size_t const tileIdx, std::size_t const tileCnt) noexcept {
|
||||
std::ignore = pushCommand(ox::make_unique<DeleteTilesCommand>(m_img, idx, tileIdx, tileCnt));
|
||||
}
|
||||
|
||||
ox::Error TileSheetEditorModel::updateSubsheet(
|
||||
TileSheet::SubSheetIdx const&idx, ox::StringViewCR name, int const cols, int const rows) noexcept {
|
||||
OX_REQUIRE(cmd, ox::makeCatch<UpdateSubSheetCommand>(m_img, idx, name, cols, rows));
|
||||
std::ignore = pushCommand(cmd);
|
||||
TileSheet::SubSheetIdx const &idx, ox::StringViewCR name, int const cols, int const rows) noexcept {
|
||||
OX_REQUIRE_M(cmd, ox::make_unique_catch<UpdateSubSheetCommand>(m_img, idx, name, cols, rows));
|
||||
std::ignore = pushCommand(std::move(cmd));
|
||||
return {};
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::setActiveSubsheet(TileSheet::SubSheetIdx const&idx) noexcept {
|
||||
void TileSheetEditorModel::setActiveSubsheet(TileSheet::SubSheetIdx const &idx) noexcept {
|
||||
m_activeSubsSheetIdx = idx;
|
||||
this->activeSubsheetChanged.emit(m_activeSubsSheetIdx);
|
||||
clearSelection();
|
||||
m_lastDrawUpdatePt = {-1, -1};
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::fill(ox::Point const&pt, int const palIdx) noexcept {
|
||||
void TileSheetEditorModel::fill(ox::Point const &pt, uint8_t const palIdx) noexcept {
|
||||
auto const&activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx);
|
||||
// build idx list
|
||||
if (pt.x >= activeSubSheet.columns * TileWidth || pt.y >= activeSubSheet.rows * TileHeight) {
|
||||
@@ -253,33 +255,33 @@ void TileSheetEditorModel::fill(ox::Point const&pt, int const palIdx) noexcept {
|
||||
if (m_ongoingDrawCommand) {
|
||||
m_updated = m_updated || m_ongoingDrawCommand->append(idxList);
|
||||
} else if (getPixel(activeSubSheet, pt) != palIdx) {
|
||||
std::ignore = pushCommand(ox::make<DrawCommand>(m_img, m_activeSubsSheetIdx, idxList, palIdx));
|
||||
std::ignore = pushCommand(ox::make_unique<DrawCommand>(m_img, m_activeSubsSheetIdx, idxList, palIdx));
|
||||
}
|
||||
}
|
||||
|
||||
ox::Error TileSheetEditorModel::rotateLeft() noexcept {
|
||||
auto &ss = activeSubSheet();
|
||||
auto const &ss = activeSubSheet();
|
||||
ox::Point pt1, pt2{ss.columns * TileWidth - 1, ss.rows * TileHeight - 1};
|
||||
if (m_selection) {
|
||||
pt1 = m_selection->a;
|
||||
pt2 = m_selection->b;
|
||||
}
|
||||
return pushCommand(ox::make<RotateCommand>(
|
||||
return pushCommand(ox::make_unique<RotateCommand>(
|
||||
m_img, m_activeSubsSheetIdx, pt1, pt2, RotateCommand::Direction::Left));
|
||||
}
|
||||
|
||||
ox::Error TileSheetEditorModel::rotateRight() noexcept {
|
||||
auto &ss = activeSubSheet();
|
||||
auto const &ss = activeSubSheet();
|
||||
ox::Point pt1, pt2{ss.columns * TileWidth - 1, ss.rows * TileHeight - 1};
|
||||
if (m_selection) {
|
||||
pt1 = m_selection->a;
|
||||
pt2 = m_selection->b;
|
||||
}
|
||||
return pushCommand(ox::make<RotateCommand>(
|
||||
return pushCommand(ox::make_unique<RotateCommand>(
|
||||
m_img, m_activeSubsSheetIdx, pt1, pt2, RotateCommand::Direction::Right));
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::setSelection(studio::Selection const&sel) noexcept {
|
||||
void TileSheetEditorModel::setSelection(studio::Selection const &sel) noexcept {
|
||||
auto const &ss = activeSubSheet();
|
||||
if (sel.a.x < ss.columns * TileWidth && sel.a.y < ss.rows * TileHeight) {
|
||||
m_selection.emplace(sel);
|
||||
@@ -290,7 +292,7 @@ void TileSheetEditorModel::setSelection(studio::Selection const&sel) noexcept {
|
||||
m_updated = true;
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::select(ox::Point const&pt) noexcept {
|
||||
void TileSheetEditorModel::select(ox::Point const &pt) noexcept {
|
||||
if (m_selTracker.updateCursorPoint(pt)) {
|
||||
setSelection(m_selTracker.selection());
|
||||
}
|
||||
@@ -313,11 +315,15 @@ void TileSheetEditorModel::clearSelection() noexcept {
|
||||
m_selection.reset();
|
||||
}
|
||||
|
||||
bool TileSheetEditorModel::hasSelection() const noexcept {
|
||||
return m_selection.has_value();
|
||||
}
|
||||
|
||||
bool TileSheetEditorModel::updated() const noexcept {
|
||||
return m_updated;
|
||||
}
|
||||
|
||||
ox::Error TileSheetEditorModel::markUpdatedCmdId(studio::UndoCommand const*cmd) noexcept {
|
||||
ox::Error TileSheetEditorModel::markUpdatedCmdId(studio::UndoCommand const *cmd) noexcept {
|
||||
m_updated = true;
|
||||
auto const cmdId = cmd->commandId();
|
||||
if (static_cast<CommandId>(cmdId) == CommandId::PaletteChange) {
|
||||
@@ -363,7 +369,7 @@ ox::Error TileSheetEditorModel::flipX() noexcept {
|
||||
a = m_selection->a;
|
||||
b = m_selection->b;
|
||||
}
|
||||
return pushCommand(ox::make<FlipXCommand>(m_img, m_activeSubsSheetIdx, a, b));
|
||||
return pushCommand(ox::make_unique<FlipXCommand>(m_img, m_activeSubsSheetIdx, a, b));
|
||||
}
|
||||
|
||||
ox::Error TileSheetEditorModel::flipY() noexcept {
|
||||
@@ -374,7 +380,7 @@ ox::Error TileSheetEditorModel::flipY() noexcept {
|
||||
a = m_selection->a;
|
||||
b = m_selection->b;
|
||||
}
|
||||
return pushCommand(ox::make<FlipYCommand>(m_img, m_activeSubsSheetIdx, a, b));
|
||||
return pushCommand(ox::make_unique<FlipYCommand>(m_img, m_activeSubsSheetIdx, a, b));
|
||||
}
|
||||
|
||||
bool TileSheetEditorModel::rotateEligible() const noexcept {
|
||||
@@ -388,14 +394,14 @@ bool TileSheetEditorModel::rotateEligible() const noexcept {
|
||||
}
|
||||
|
||||
ox::Error TileSheetEditorModel::moveSubSheet(TileSheet::SubSheetIdx src, TileSheet::SubSheetIdx dst) noexcept {
|
||||
return pushCommand(ox::make<MoveSubSheetCommand>(m_img, std::move(src), std::move(dst)));
|
||||
return pushCommand(ox::make_unique<MoveSubSheetCommand>(m_img, std::move(src), std::move(dst)));
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::getFillPixels(
|
||||
TileSheet::SubSheet const&activeSubSheet,
|
||||
ox::Span<bool> pixels,
|
||||
ox::Point const&pt,
|
||||
int const oldColor) const noexcept {
|
||||
ox::Span<bool> const pixels,
|
||||
ox::Point const &pt,
|
||||
uint8_t const oldColor) noexcept {
|
||||
auto const idx = ptToIdx(pt, activeSubSheet.columns);
|
||||
auto const relIdx = idx % PixelsPerTile;
|
||||
if (pixels[relIdx] || activeSubSheet.pixels[idx] != oldColor) {
|
||||
@@ -435,9 +441,9 @@ void TileSheetEditorModel::setPalPath() noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
ox::Error TileSheetEditorModel::pushCommand(studio::UndoCommand *cmd) noexcept {
|
||||
std::ignore = m_undoStack.push(ox::UPtr{cmd});
|
||||
m_ongoingDrawCommand = dynamic_cast<DrawCommand*>(cmd);
|
||||
ox::Error TileSheetEditorModel::pushCommand(ox::UPtr<studio::UndoCommand> &&cmd) noexcept {
|
||||
m_ongoingDrawCommand = dynamic_cast<DrawCommand*>(cmd.get());
|
||||
std::ignore = m_undoStack.push(std::move(cmd));
|
||||
m_updated = true;
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
namespace nostalgia::gfx {
|
||||
|
||||
class TileSheetEditorModel: public ox::SignalHandler {
|
||||
class TileSheetEditorModel final: public ox::SignalHandler {
|
||||
|
||||
public:
|
||||
ox::Signal<ox::Error(const TileSheet::SubSheetIdx&)> activeSubsheetChanged;
|
||||
@@ -35,7 +35,7 @@ class TileSheetEditorModel: public ox::SignalHandler {
|
||||
studio::SelectionTracker m_selTracker;
|
||||
ox::Optional<studio::Selection> m_selection;
|
||||
ox::Point m_lineStartPt;
|
||||
ox::Point m_lastDrawUpdatePt;
|
||||
ox::Point m_lastDrawUpdatePt{-1, -1};
|
||||
bool m_updated = false;
|
||||
|
||||
public:
|
||||
@@ -53,13 +53,13 @@ class TileSheetEditorModel: public ox::SignalHandler {
|
||||
bool acceptsClipboardPayload() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr TileSheet const&img() const noexcept;
|
||||
constexpr TileSheet const &img() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr TileSheet &img() noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr Palette const&pal() const noexcept;
|
||||
constexpr Palette const &pal() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
ox::String const &palPath() const & noexcept;
|
||||
@@ -71,26 +71,26 @@ class TileSheetEditorModel: public ox::SignalHandler {
|
||||
[[nodiscard]]
|
||||
size_t palettePage() const noexcept;
|
||||
|
||||
void drawCommand(ox::Point const&pt, std::size_t palIdx) noexcept;
|
||||
void drawCommand(ox::Point const &pt, std::size_t palIdx) noexcept;
|
||||
|
||||
void drawLineCommand(ox::Point const&pt, std::size_t palIdx) noexcept;
|
||||
void drawLineCommand(ox::Point const &pt, std::size_t palIdx) noexcept;
|
||||
|
||||
void endDrawCommand() noexcept;
|
||||
|
||||
void addSubsheet(TileSheet::SubSheetIdx const&parentIdx) noexcept;
|
||||
void addSubsheet(TileSheet::SubSheetIdx const &parentIdx) noexcept;
|
||||
|
||||
void rmSubsheet(TileSheet::SubSheetIdx const&idx) noexcept;
|
||||
void rmSubsheet(TileSheet::SubSheetIdx const &idx) noexcept;
|
||||
|
||||
void insertTiles(TileSheet::SubSheetIdx const&idx, std::size_t tileIdx, std::size_t tileCnt) noexcept;
|
||||
void insertTiles(TileSheet::SubSheetIdx const &idx, std::size_t tileIdx, std::size_t tileCnt) noexcept;
|
||||
|
||||
void deleteTiles(TileSheet::SubSheetIdx const&idx, std::size_t tileIdx, std::size_t tileCnt) noexcept;
|
||||
void deleteTiles(TileSheet::SubSheetIdx const &idx, std::size_t tileIdx, std::size_t tileCnt) noexcept;
|
||||
|
||||
ox::Error updateSubsheet(TileSheet::SubSheetIdx const&idx, ox::StringView const&name, int cols, int rows) noexcept;
|
||||
ox::Error updateSubsheet(TileSheet::SubSheetIdx const &idx, ox::StringView const &name, int cols, int rows) noexcept;
|
||||
|
||||
void setActiveSubsheet(TileSheet::SubSheetIdx const&) noexcept;
|
||||
void setActiveSubsheet(TileSheet::SubSheetIdx const &) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
TileSheet::SubSheet const&activeSubSheet() const noexcept {
|
||||
TileSheet::SubSheet const &activeSubSheet() const noexcept {
|
||||
return getSubSheet(m_img, m_activeSubsSheetIdx);
|
||||
}
|
||||
|
||||
@@ -100,28 +100,31 @@ class TileSheetEditorModel: public ox::SignalHandler {
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr TileSheet::SubSheetIdx const&activeSubSheetIdx() const noexcept {
|
||||
constexpr TileSheet::SubSheetIdx const &activeSubSheetIdx() const noexcept {
|
||||
return m_activeSubsSheetIdx;
|
||||
}
|
||||
|
||||
void fill(ox::Point const&pt, int palIdx) noexcept;
|
||||
void fill(ox::Point const &pt, uint8_t palIdx) noexcept;
|
||||
|
||||
ox::Error rotateLeft() noexcept;
|
||||
|
||||
ox::Error rotateRight() noexcept;
|
||||
|
||||
void setSelection(studio::Selection const&sel) noexcept;
|
||||
void setSelection(studio::Selection const &sel) noexcept;
|
||||
|
||||
void select(ox::Point const&pt) noexcept;
|
||||
void select(ox::Point const &pt) noexcept;
|
||||
|
||||
void completeSelection() noexcept;
|
||||
|
||||
void clearSelection() noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
bool hasSelection() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
bool updated() const noexcept;
|
||||
|
||||
ox::Error markUpdatedCmdId(studio::UndoCommand const*cmd) noexcept;
|
||||
ox::Error markUpdatedCmdId(studio::UndoCommand const *cmd) noexcept;
|
||||
|
||||
ox::Error markUpdated() noexcept;
|
||||
|
||||
@@ -144,21 +147,21 @@ class TileSheetEditorModel: public ox::SignalHandler {
|
||||
ox::Error moveSubSheet(TileSheet::SubSheetIdx src, TileSheet::SubSheetIdx dst) noexcept;
|
||||
|
||||
private:
|
||||
void getFillPixels(
|
||||
TileSheet::SubSheet const&activeSubSheet,
|
||||
static void getFillPixels(
|
||||
TileSheet::SubSheet const &activeSubSheet,
|
||||
ox::Span<bool> pixels,
|
||||
ox::Point const&pt,
|
||||
int oldColor) const noexcept;
|
||||
ox::Point const &pt,
|
||||
uint8_t oldColor) noexcept;
|
||||
|
||||
void setPalPath() noexcept;
|
||||
|
||||
ox::Error pushCommand(studio::UndoCommand *cmd) noexcept;
|
||||
ox::Error pushCommand(ox::UPtr<studio::UndoCommand> &&cmd) noexcept;
|
||||
|
||||
ox::Error handleFileRename(ox::StringViewCR, ox::StringViewCR newPath, ox::UUID const&id) noexcept;
|
||||
ox::Error handleFileRename(ox::StringViewCR, ox::StringViewCR newPath, ox::UUID const &id) noexcept;
|
||||
|
||||
};
|
||||
|
||||
constexpr TileSheet const&TileSheetEditorModel::img() const noexcept {
|
||||
constexpr TileSheet const &TileSheetEditorModel::img() const noexcept {
|
||||
return m_img;
|
||||
}
|
||||
|
||||
@@ -166,7 +169,7 @@ constexpr TileSheet &TileSheetEditorModel::img() noexcept {
|
||||
return m_img;
|
||||
}
|
||||
|
||||
constexpr Palette const&TileSheetEditorModel::pal() const noexcept {
|
||||
constexpr Palette const &TileSheetEditorModel::pal() const noexcept {
|
||||
if (m_pal) {
|
||||
return *m_pal;
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ void TileSheetEditorView::clickSelect(ox::Vec2 const&paneSize, ox::Vec2 const&cl
|
||||
|
||||
void TileSheetEditorView::clickFill(ox::Vec2 const&paneSize, ox::Vec2 const&clickPos) noexcept {
|
||||
auto const pt = clickPoint(paneSize, clickPos);
|
||||
m_model.fill(pt, static_cast<int>(m_palIdx));
|
||||
m_model.fill(pt, static_cast<uint8_t>(m_palIdx));
|
||||
}
|
||||
|
||||
void TileSheetEditorView::releaseMouseButton(TileSheetTool tool) noexcept {
|
||||
|
||||
@@ -15,7 +15,7 @@ target_link_libraries(
|
||||
|
||||
target_compile_definitions(
|
||||
NostalgiaStudio PUBLIC
|
||||
OLYMPIC_APP_VERSION="dev build"
|
||||
OLYMPIC_APP_VERSION="d2025.06.0"
|
||||
)
|
||||
|
||||
install(
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<string>APPL</string>
|
||||
|
||||
<key>CFBundleVersion</key>
|
||||
<string>dev build</string>
|
||||
<string>d2025.06.0</string>
|
||||
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>12.0.0</string>
|
||||
|
||||
@@ -154,7 +154,7 @@ class AssetRef: public ox::SignalHandler {
|
||||
private:
|
||||
constexpr ox::Error emitUpdated() const noexcept {
|
||||
updated.emit();
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -60,6 +60,11 @@ ox::Result<ox::CStringView> uuidToPath(Context &ctx, ox::StringViewCR uuid) noex
|
||||
|
||||
ox::Result<ox::CStringView> uuidToPath(Context &ctx, ox::UUID const&uuid) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool isUuidUrl(ox::StringViewCR path) noexcept {
|
||||
return ox::beginsWith(path, "uuid://");
|
||||
}
|
||||
|
||||
#ifndef OX_BARE_METAL
|
||||
|
||||
namespace detail {
|
||||
|
||||
@@ -260,7 +260,7 @@ ox::Result<DstType> convertObjToObj(
|
||||
template<typename DstType>
|
||||
ox::Result<DstType> convert(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>
|
||||
|
||||
@@ -10,6 +10,6 @@
|
||||
|
||||
namespace studio {
|
||||
|
||||
void registerModule(studio::Module const*) noexcept;
|
||||
void registerModule(Module const*) noexcept;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
add_library(
|
||||
StudioAppLib
|
||||
aboutpopup.cpp
|
||||
app.cpp
|
||||
clawviewer.cpp
|
||||
deleteconfirmation.cpp
|
||||
filedialogmanager.cpp
|
||||
font.cpp
|
||||
makecopypopup.cpp
|
||||
newdir.cpp
|
||||
newmenu.cpp
|
||||
newproject.cpp
|
||||
popups/about.cpp
|
||||
popups/deleteconfirmation.cpp
|
||||
popups/makecopy.cpp
|
||||
popups/newdir.cpp
|
||||
popups/newmenu.cpp
|
||||
popups/newproject.cpp
|
||||
popups/renamefile.cpp
|
||||
projectexplorer.cpp
|
||||
renamefile.cpp
|
||||
studioui.cpp
|
||||
)
|
||||
target_compile_definitions(
|
||||
|
||||
@@ -28,10 +28,15 @@ class StudioUIDrawer: public turbine::gl::Drawer {
|
||||
};
|
||||
|
||||
static void keyEventHandler(turbine::Context &ctx, turbine::Key key, bool down) noexcept {
|
||||
auto sctx = turbine::applicationData<studio::Context>(ctx);
|
||||
auto const sctx = turbine::applicationData<studio::Context>(ctx);
|
||||
sctx->ui.handleKeyEvent(key, down);
|
||||
}
|
||||
|
||||
static void mouseButtonEventHandler(turbine::Context &ctx, int const btn, bool const down) noexcept {
|
||||
auto const sctx = turbine::applicationData<studio::Context>(ctx);
|
||||
sctx->ui.handleMouseButtonEvent(btn, down);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
ox::Vector<ox::SpanView<uint8_t>> WindowIcons() noexcept;
|
||||
|
||||
@@ -43,7 +48,8 @@ static ox::Error runApp(
|
||||
oxLogError(turbine::setWindowIcon(*ctx, WindowIcons()));
|
||||
turbine::setWindowTitle(*ctx, keelCtx(*ctx).appName);
|
||||
turbine::setKeyEventHandler(*ctx, keyEventHandler);
|
||||
turbine::setRefreshWithin(*ctx, 0);
|
||||
turbine::setMouseButtonEventHandler(*ctx, mouseButtonEventHandler);
|
||||
turbine::requireRefreshWithin(*ctx, 0);
|
||||
StudioUI ui(*ctx, projectDataDir);
|
||||
StudioUIDrawer drawer(ui);
|
||||
turbine::gl::addDrawer(*ctx, &drawer);
|
||||
@@ -71,9 +77,9 @@ static ox::Error run(
|
||||
}
|
||||
|
||||
ox::Error run(
|
||||
ox::StringView project,
|
||||
ox::StringView appName,
|
||||
ox::StringView projectDataDir,
|
||||
ox::StringView const project,
|
||||
ox::StringView const appName,
|
||||
ox::StringView const projectDataDir,
|
||||
ox::SpanView<ox::CString> args) noexcept {
|
||||
return studio::run(ox::sfmt("{} {}", project, appName), projectDataDir, args);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <imgui.h>
|
||||
|
||||
#include <studio/imguiutil.hpp>
|
||||
#include "aboutpopup.hpp"
|
||||
#include "about.hpp"
|
||||
|
||||
namespace olympic {
|
||||
extern ox::String appVersion;
|
||||
@@ -15,7 +15,11 @@ namespace studio {
|
||||
|
||||
AboutPopup::AboutPopup(turbine::Context &ctx) noexcept:
|
||||
Popup("About"),
|
||||
#ifdef DEBUG
|
||||
m_text{sfmt("{} [DEBUG] - {}", keelCtx(ctx).appName, olympic::appVersion)} {
|
||||
#else
|
||||
m_text{sfmt("{} - {}", keelCtx(ctx).appName, olympic::appVersion)} {
|
||||
#endif
|
||||
}
|
||||
|
||||
void AboutPopup::draw(Context &sctx) noexcept {
|
||||
@@ -2,6 +2,8 @@
|
||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <turbine/gfx.hpp>
|
||||
|
||||
#include <studio/imguiutil.hpp>
|
||||
|
||||
#include "deleteconfirmation.hpp"
|
||||
@@ -43,6 +45,9 @@ void DeleteConfirmation::draw(Context &ctx) noexcept {
|
||||
ImGui::OpenPopup(title().c_str());
|
||||
m_stage = Stage::Open;
|
||||
m_open = true;
|
||||
// require extended refresh in case new contents require resize
|
||||
// this can take around a second
|
||||
turbine::requireRefreshFor(ctx.tctx, 1000);
|
||||
[[fallthrough]];
|
||||
case Stage::Open:
|
||||
drawWindow(ctx.tctx, m_open, [this] {
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include <studio/imguiutil.hpp>
|
||||
|
||||
#include "makecopypopup.hpp"
|
||||
#include "makecopy.hpp"
|
||||
|
||||
namespace studio {
|
||||
|
||||
@@ -80,9 +80,9 @@ void NewMenu::addItemMaker(ox::UPtr<ItemMaker> &&im) noexcept {
|
||||
});
|
||||
}
|
||||
|
||||
void NewMenu::installItemTemplate(ox::UPtr<ItemTemplate> &tmplt) noexcept {
|
||||
void NewMenu::installItemTemplate(ox::UPtr<ItemTemplate> &&tmplt) noexcept {
|
||||
for (auto const&im : m_types) {
|
||||
if (im->installTemplate(tmplt)) {
|
||||
if (im->installTemplate(std::move(tmplt))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -72,7 +72,7 @@ class NewMenu final: public Popup {
|
||||
|
||||
void addItemMaker(ox::UPtr<ItemMaker> &&im) noexcept;
|
||||
|
||||
void installItemTemplate(ox::UPtr<ItemTemplate> &tmplt) noexcept;
|
||||
void installItemTemplate(ox::UPtr<ItemTemplate> &&tmplt) noexcept;
|
||||
|
||||
private:
|
||||
void drawNewItemType(Context const&sctx) noexcept;
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <studio/imguiutil.hpp>
|
||||
#include <utility>
|
||||
|
||||
#include "filedialogmanager.hpp"
|
||||
#include "../filedialogmanager.hpp"
|
||||
#include "newproject.hpp"
|
||||
|
||||
namespace studio {
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include <ox/std/def.hpp>
|
||||
|
||||
#include <keel/media.hpp>
|
||||
#include <glutils/glutils.hpp>
|
||||
#include <turbine/turbine.hpp>
|
||||
@@ -17,6 +19,8 @@
|
||||
#include "font.hpp"
|
||||
#include "studioui.hpp"
|
||||
|
||||
#include <complex>
|
||||
|
||||
#ifdef OX_OS_Darwin
|
||||
#define STUDIO_CTRL "Cmd"
|
||||
#else
|
||||
@@ -30,18 +34,6 @@ static bool shutdownHandler(turbine::Context &ctx) {
|
||||
return sctx->ui.handleShutdown();
|
||||
}
|
||||
|
||||
void navigateTo(Context &ctx, ox::StringParam filePath, ox::StringParam navArgs) noexcept {
|
||||
ox::String path = std::move(filePath);
|
||||
if (beginsWith(path, "uuid://")) {
|
||||
auto [p, err] = keel::uuidUrlToPath(keelCtx(ctx), path);
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
path = p;
|
||||
}
|
||||
ctx.ui.navigateTo(std::move(path), std::move(navArgs));
|
||||
}
|
||||
|
||||
namespace ig {
|
||||
extern bool s_mainWinHasFocus;
|
||||
}
|
||||
@@ -54,7 +46,7 @@ void registerModule(Module const*mod) noexcept {
|
||||
}
|
||||
|
||||
|
||||
struct StudioConfig {
|
||||
struct StudioConfigV1 {
|
||||
static constexpr auto TypeName = "net.drinkingtea.studio.StudioConfig";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
ox::String projectPath;
|
||||
@@ -63,16 +55,67 @@ struct StudioConfig {
|
||||
bool showProjectExplorer = true;
|
||||
};
|
||||
|
||||
OX_MODEL_BEGIN(StudioConfig)
|
||||
OX_MODEL_BEGIN(StudioConfigV1)
|
||||
OX_MODEL_FIELD_RENAME(activeTabItemName, active_tab_item_name)
|
||||
OX_MODEL_FIELD_RENAME(projectPath, project_path)
|
||||
OX_MODEL_FIELD_RENAME(openFiles, open_files)
|
||||
OX_MODEL_FIELD_RENAME(showProjectExplorer, show_project_explorer)
|
||||
OX_MODEL_END()
|
||||
|
||||
StudioUI::StudioUI(turbine::Context &ctx, ox::StringParam projectDataDir) noexcept:
|
||||
m_sctx{*this, ctx},
|
||||
m_tctx{ctx},
|
||||
|
||||
struct StudioConfigV2 {
|
||||
static constexpr auto TypeName = "net.drinkingtea.studio.StudioConfig";
|
||||
static constexpr auto TypeVersion = 2;
|
||||
struct ProjectConfig {
|
||||
static constexpr auto TypeName = "net.drinkingtea.studio.ProjectConfig";
|
||||
static constexpr auto TypeVersion = 2;
|
||||
ox::String projectPath;
|
||||
ox::String activeTabItemName;
|
||||
ox::Vector<ox::String> openFiles;
|
||||
};
|
||||
ox::Vector<ProjectConfig> projects;
|
||||
bool showProjectExplorer = true;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr ProjectConfig const *project() const {
|
||||
return projects.empty() ? nullptr : &projects[0];
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr ProjectConfig *project() {
|
||||
return projects.empty() ? nullptr : &projects[0];
|
||||
}
|
||||
};
|
||||
|
||||
OX_MODEL_BEGIN(StudioConfigV2::ProjectConfig)
|
||||
OX_MODEL_FIELD_RENAME(activeTabItemName, active_tab_item_name)
|
||||
OX_MODEL_FIELD_RENAME(projectPath, project_path)
|
||||
OX_MODEL_FIELD_RENAME(openFiles, open_files)
|
||||
OX_MODEL_END()
|
||||
|
||||
OX_MODEL_BEGIN(StudioConfigV2)
|
||||
OX_MODEL_FIELD(projects)
|
||||
OX_MODEL_FIELD_RENAME(showProjectExplorer, show_project_explorer)
|
||||
OX_MODEL_END()
|
||||
|
||||
static ox::Error convertStudioConfigV1ToStudioConfigV2(
|
||||
keel::Context&,
|
||||
StudioConfigV1 &src,
|
||||
StudioConfigV2 &dst) noexcept {
|
||||
dst.projects.emplace_back(StudioConfigV2::ProjectConfig{
|
||||
.projectPath = std::move(src.projectPath),
|
||||
.activeTabItemName = std::move(src.activeTabItemName),
|
||||
.openFiles = std::move(src.openFiles),
|
||||
});
|
||||
dst.showProjectExplorer = src.showProjectExplorer;
|
||||
return {};
|
||||
}
|
||||
|
||||
using StudioConfig = StudioConfigV2;
|
||||
|
||||
|
||||
StudioUI::StudioUI(turbine::Context &tctx, ox::StringParam projectDataDir) noexcept:
|
||||
m_sctx{*this, tctx},
|
||||
m_projectDataDir{std::move(projectDataDir)} {
|
||||
{
|
||||
ImFontConfig fontCfg;
|
||||
@@ -83,12 +126,18 @@ StudioUI::StudioUI(turbine::Context &ctx, ox::StringParam projectDataDir) noexce
|
||||
// but AddFontFromMemoryTTF requires a mutable buffer.
|
||||
// However, setting fontCfg.FontDataOwnedByAtlas ensures
|
||||
// that it will still be treated as const.
|
||||
// ImGui documentation recognize that this is a bad design,
|
||||
// ImGui documentation recognizes that this is a bad design,
|
||||
// and hopefully it will change at some point.
|
||||
io.Fonts->AddFontFromMemoryTTF(const_cast<uint8_t*>(font.data()), static_cast<int>(font.size()), 13, &fontCfg);
|
||||
}
|
||||
auto &kctx = keelCtx(m_tctx);
|
||||
kctx.converters.emplace_back(keel::Converter::make<convertStudioConfigV1ToStudioConfigV2>());
|
||||
oxLogError(headerizeConfigFile<StudioConfigV1>(kctx));
|
||||
turbine::setApplicationData(m_tctx, &m_sctx);
|
||||
turbine::setShutdownHandler(m_tctx, shutdownHandler);
|
||||
m_sctx.navCallback = [this](ox::StringParam filePath, ox::StringParam navArgs) {
|
||||
handleNavigationChange(std::move(filePath), std::move(navArgs));
|
||||
};
|
||||
m_projectExplorer.fileChosen.connect(this, &StudioUI::openFile);
|
||||
m_projectExplorer.addDir.connect(this, &StudioUI::addDir);
|
||||
m_projectExplorer.addItem.connect(this, &StudioUI::addFile);
|
||||
@@ -102,21 +151,14 @@ StudioUI::StudioUI(turbine::Context &ctx, ox::StringParam projectDataDir) noexce
|
||||
m_newMenu.finished.connect(this, &StudioUI::openFile);
|
||||
m_closeAppConfirm.response.connect(this, &StudioUI::handleCloseAppResponse);
|
||||
m_closeFileConfirm.response.connect(this, &StudioUI::handleCloseFileResponse);
|
||||
m_removeRecentProject.dlg.response.connect(this, &StudioUI::handleRemoveRecentProjectResponse);
|
||||
loadModules();
|
||||
// open project and files
|
||||
auto const [config, err] = studio::readConfig<StudioConfig>(keelCtx(m_tctx));
|
||||
m_showProjectExplorer = config.showProjectExplorer;
|
||||
if (!err) {
|
||||
auto const openProjErr = openProjectPath(config.projectPath);
|
||||
if (!openProjErr) {
|
||||
for (auto const&f: config.openFiles) {
|
||||
auto const openFileErr = openFileActiveTab(f, config.activeTabItemName == f);
|
||||
if (openFileErr) {
|
||||
oxErrorf("\nCould not open editor for file:\n\t{}\nReason:\n\t{}\n", f, toStr(openFileErr));
|
||||
continue;
|
||||
}
|
||||
m_activeEditor = m_editors.back().value->get();
|
||||
}
|
||||
if (auto const pc = config.project()) {
|
||||
oxLogError(openProjectPath(pc->projectPath));
|
||||
}
|
||||
} else {
|
||||
if constexpr(!ox::defines::Debug) {
|
||||
@@ -135,14 +177,24 @@ void StudioUI::handleKeyEvent(turbine::Key const key, bool const down) noexcept
|
||||
}
|
||||
}
|
||||
|
||||
void StudioUI::navigateTo(ox::StringParam path, ox::StringParam navArgs) noexcept {
|
||||
void StudioUI::handleMouseButtonEvent(int const btn, bool const down) noexcept {
|
||||
if (down) {
|
||||
if (btn == 3) { // back button
|
||||
navigateBack(m_sctx);
|
||||
} else if (btn == 4) { // forward button
|
||||
navigateForward(m_sctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StudioUI::handleNavigationChange(ox::StringParam path, ox::StringParam navArgs) noexcept {
|
||||
m_navAction.emplace(std::move(path), std::move(navArgs));
|
||||
}
|
||||
|
||||
void StudioUI::draw() noexcept {
|
||||
glutils::clearScreen();
|
||||
drawMenu();
|
||||
auto const&viewport = *ImGui::GetMainViewport();
|
||||
auto const &viewport = *ImGui::GetMainViewport();
|
||||
ImGui::SetNextWindowPos(viewport.WorkPos);
|
||||
ImGui::SetNextWindowSize(viewport.WorkSize);
|
||||
ImGui::SetNextWindowViewport(viewport.ID);
|
||||
@@ -194,6 +246,18 @@ void StudioUI::drawMenu() noexcept {
|
||||
if (ImGui::MenuItem("Open Project...", STUDIO_CTRL "+O")) {
|
||||
m_taskRunner.add(*ox::make<FileDialogManager>(this, &StudioUI::openProjectPath));
|
||||
}
|
||||
if (ImGui::BeginMenu("Recent Projects", m_recentProjects.size() > 1)) {
|
||||
for (size_t i = 1; i < m_recentProjects.size(); ++i) {
|
||||
auto const &p = m_recentProjects[i];
|
||||
if (ImGui::MenuItem(p.c_str())) {
|
||||
if (openProjectPath(p)) {
|
||||
m_removeRecentProject.idx = i;
|
||||
m_removeRecentProject.dlg.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::MenuItem(
|
||||
"Save",
|
||||
STUDIO_CTRL "+S",
|
||||
@@ -207,7 +271,7 @@ void StudioUI::drawMenu() noexcept {
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("Edit")) {
|
||||
auto undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr;
|
||||
auto const undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr;
|
||||
if (ImGui::MenuItem(
|
||||
"Undo", STUDIO_CTRL "+Z", false, undoStack && undoStack->canUndo())) {
|
||||
oxLogError(undoStack->undo());
|
||||
@@ -247,6 +311,19 @@ void StudioUI::drawMenu() noexcept {
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("Navigate")) {
|
||||
constexpr auto backShortcut = ox::defines::OS == ox::OS::Darwin ? "Cmd+[" : "Alt+Left Arrow";
|
||||
constexpr auto fwdShortcut = ox::defines::OS == ox::OS::Darwin ? "Cmd+]" : "Alt+Right Arrow";
|
||||
if (ImGui::MenuItem("Back", backShortcut, false, m_sctx.navIdx > 1)) {
|
||||
navigateBack(m_sctx);
|
||||
}
|
||||
if (ImGui::MenuItem(
|
||||
"Forward", fwdShortcut, false,
|
||||
m_sctx.navIdx < m_sctx.navStack.size())) {
|
||||
navigateForward(m_sctx);
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("Help")) {
|
||||
if (ImGui::MenuItem("About")) {
|
||||
m_aboutPopup.open();
|
||||
@@ -270,7 +347,7 @@ void StudioUI::drawTabBar() noexcept {
|
||||
|
||||
void StudioUI::drawTabs() noexcept {
|
||||
for (auto it = m_editors.begin(); it != m_editors.end();) {
|
||||
auto const&e = *it;
|
||||
auto const &e = *it;
|
||||
auto open = true;
|
||||
auto const unsavedChanges = e->unsavedChanges() ? ImGuiTabItemFlags_UnsavedDocument : 0;
|
||||
auto const selected = m_activeEditorUpdatePending == e.get() ? ImGuiTabItemFlags_SetSelected : 0;
|
||||
@@ -279,19 +356,27 @@ void StudioUI::drawTabs() noexcept {
|
||||
if (m_activeEditor != e.get()) [[unlikely]] {
|
||||
m_activeEditor = e.get();
|
||||
studio::editConfig<StudioConfig>(keelCtx(m_tctx), [&](StudioConfig &config) {
|
||||
config.activeTabItemName = m_activeEditor->itemPath();
|
||||
if (auto const pc = config.project()) {
|
||||
pc->activeTabItemName = m_activeEditor->itemPath();
|
||||
}
|
||||
});
|
||||
turbine::setRefreshWithin(m_tctx, 0);
|
||||
turbine::requireRefreshWithin(m_tctx, 0);
|
||||
} else [[likely]] {
|
||||
if (m_activeEditorUpdatePending == e.get()) [[unlikely]] {
|
||||
m_activeEditorUpdatePending = nullptr;
|
||||
}
|
||||
if (m_activeEditorOnLastDraw != e.get()) [[unlikely]] {
|
||||
m_activeEditor->onActivated();
|
||||
if (!m_sctx.navIdx ||
|
||||
(m_sctx.navIdx <= m_sctx.navStack.size() &&
|
||||
m_sctx.navStack[m_sctx.navIdx - 1].filePath != m_activeEditor->itemPath())) {
|
||||
m_sctx.navStack.resize(m_sctx.navIdx);
|
||||
++m_sctx.navIdx;
|
||||
m_sctx.navStack.emplace_back(ox::String{m_activeEditor->itemPath()}, ox::String{""});
|
||||
}
|
||||
}
|
||||
if (m_closeActiveTab) [[unlikely]] {
|
||||
ImGui::SetTabItemClosed(e->itemDisplayName().c_str());
|
||||
|
||||
} else if (open) [[likely]] {
|
||||
e->draw(m_sctx);
|
||||
}
|
||||
@@ -331,20 +416,21 @@ void StudioUI::drawTabs() noexcept {
|
||||
m_closeActiveTab = false;
|
||||
}
|
||||
if (m_navAction) {
|
||||
oxLogError(openFile(m_navAction->path));
|
||||
m_activeEditor->navigateTo(m_navAction->args);
|
||||
m_navAction.reset();
|
||||
if (!openFile(m_navAction->path)) {
|
||||
m_activeEditor->navigateTo(m_navAction->args);
|
||||
m_navAction.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StudioUI::loadEditorMaker(EditorMaker const&editorMaker) noexcept {
|
||||
for (auto const&ext : editorMaker.fileTypes) {
|
||||
void StudioUI::loadEditorMaker(EditorMaker const &editorMaker) noexcept {
|
||||
for (auto const &ext : editorMaker.fileTypes) {
|
||||
m_editorMakers[ext] = editorMaker.make;
|
||||
}
|
||||
}
|
||||
|
||||
void StudioUI::loadModule(Module const&mod) noexcept {
|
||||
for (auto const&editorMaker : mod.editors(m_sctx)) {
|
||||
void StudioUI::loadModule(Module const &mod) noexcept {
|
||||
for (auto const &editorMaker : mod.editors(m_sctx)) {
|
||||
loadEditorMaker(editorMaker);
|
||||
}
|
||||
for (auto &im : mod.itemMakers(m_sctx)) {
|
||||
@@ -352,7 +438,7 @@ void StudioUI::loadModule(Module const&mod) noexcept {
|
||||
}
|
||||
auto tmplts = mod.itemTemplates(m_sctx);
|
||||
for (auto &t : tmplts) {
|
||||
m_newMenu.installItemTemplate(t);
|
||||
m_newMenu.installItemTemplate(std::move(t));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,6 +528,22 @@ void StudioUI::handleKeyInput() noexcept {
|
||||
}
|
||||
}
|
||||
}
|
||||
if constexpr (ox::defines::OS == ox::OS::Darwin) {
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_LeftBracket)) {
|
||||
navigateBack(m_sctx);
|
||||
} else if (ImGui::IsKeyPressed(ImGuiKey_RightBracket)) {
|
||||
navigateForward(m_sctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
if constexpr (ox::defines::OS != ox::OS::Darwin) {
|
||||
if (ImGui::IsKeyDown(ImGuiKey_ModAlt)) {
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow)) {
|
||||
navigateBack(m_sctx);
|
||||
} else if (ImGui::IsKeyPressed(ImGuiKey_RightArrow)) {
|
||||
navigateForward(m_sctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -464,14 +566,16 @@ ox::Error StudioUI::renameFile(ox::StringViewCR path) noexcept {
|
||||
return m_renameFile.openPath(path);
|
||||
}
|
||||
|
||||
ox::Error StudioUI::handleMoveFile(ox::StringViewCR oldPath, ox::StringViewCR newPath, ox::UUID const&) noexcept {
|
||||
ox::Error StudioUI::handleMoveFile(ox::StringViewCR oldPath, ox::StringViewCR newPath, ox::UUID const &) noexcept {
|
||||
for (auto &f : m_openFiles) {
|
||||
if (f == oldPath) {
|
||||
f = newPath;
|
||||
editConfig<StudioConfig>(keelCtx(m_sctx), [&](StudioConfig &cfg) {
|
||||
auto p = find(cfg.openFiles.begin(), cfg.openFiles.end(), oldPath);
|
||||
*p = newPath;
|
||||
cfg.activeTabItemName = newPath;
|
||||
if (auto const pc = cfg.project()) {
|
||||
auto p = find(pc->openFiles.begin(), pc->openFiles.end(), oldPath);
|
||||
*p = newPath;
|
||||
pc->activeTabItemName = newPath;
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
@@ -485,24 +589,36 @@ ox::Error StudioUI::handleMoveFile(ox::StringViewCR oldPath, ox::StringViewCR ne
|
||||
|
||||
ox::Error StudioUI::handleDeleteDir(ox::StringViewCR path) noexcept {
|
||||
auto const p = sfmt("{}/", path);
|
||||
for (auto &e : m_editors) {
|
||||
if (beginsWith(e->itemPath(), p)) {
|
||||
oxLogError(closeFile(path));
|
||||
m_closeActiveTab = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::ignore = m_editors.erase(
|
||||
std::remove_if(
|
||||
m_editors.begin(), m_editors.end(),
|
||||
[&](ox::UPtr<BaseEditor> const &e) {
|
||||
if (beginsWith(e->itemPath(), p)) {
|
||||
oxLogError(closeFile(path));
|
||||
if (e.get() != m_activeEditor) {
|
||||
return true;
|
||||
}
|
||||
m_closeActiveTab = true;
|
||||
}
|
||||
return false;
|
||||
}));
|
||||
return m_projectExplorer.refreshProjectTreeModel();
|
||||
}
|
||||
|
||||
ox::Error StudioUI::handleDeleteFile(ox::StringViewCR path) noexcept {
|
||||
for (auto &e : m_editors) {
|
||||
if (path == e->itemPath()) {
|
||||
oxLogError(closeFile(path));
|
||||
m_closeActiveTab = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::ignore = m_editors.erase(
|
||||
std::remove_if(
|
||||
m_editors.begin(), m_editors.end(),
|
||||
[&](ox::UPtr<BaseEditor> const &e) {
|
||||
if (path == e->itemPath()) {
|
||||
oxLogError(closeFile(path));
|
||||
if (e.get() != m_activeEditor) {
|
||||
return true;
|
||||
}
|
||||
m_closeActiveTab = true;
|
||||
}
|
||||
return false;
|
||||
}));
|
||||
return m_projectExplorer.refreshProjectTreeModel();
|
||||
}
|
||||
|
||||
@@ -533,6 +649,9 @@ ox::Error StudioUI::openProjectPath(ox::StringParam path) noexcept {
|
||||
ox::make_unique_catch<Project>(keelCtx(m_tctx), std::move(path), m_projectDataDir)
|
||||
.moveTo(m_project));
|
||||
m_sctx.project = m_project.get();
|
||||
m_activeEditor = nullptr;
|
||||
m_activeEditorOnLastDraw = nullptr;
|
||||
m_activeEditorUpdatePending = nullptr;
|
||||
turbine::setWindowTitle(
|
||||
m_tctx, ox::sfmt("{} - {}", keelCtx(m_tctx).appName, m_project->projectPath()));
|
||||
m_deleteConfirmation.deleteFile.connect(m_sctx.project, &Project::deleteItem);
|
||||
@@ -546,8 +665,36 @@ ox::Error StudioUI::openProjectPath(ox::StringParam path) noexcept {
|
||||
m_openFiles.clear();
|
||||
m_editors.clear();
|
||||
studio::editConfig<StudioConfig>(keelCtx(m_tctx), [&](StudioConfig &config) {
|
||||
config.projectPath = ox::String(m_project->projectPath());
|
||||
config.openFiles.clear();
|
||||
auto const pcIt = std::find_if(
|
||||
config.projects.begin(), config.projects.end(),
|
||||
[this](StudioConfig::ProjectConfig const &pc) {
|
||||
return pc.projectPath == m_project->projectPath();
|
||||
});
|
||||
if (pcIt != config.projects.end()) {
|
||||
auto p = std::move(*pcIt);
|
||||
std::ignore = config.projects.erase(pcIt);
|
||||
auto &pc = *config.projects.emplace(0, std::move(p));
|
||||
for (auto const &f: pc.openFiles) {
|
||||
auto const openFileErr = openFileActiveTab(f, pc.activeTabItemName == f);
|
||||
if (openFileErr) {
|
||||
oxErrorf("\nCould not open editor for file:\n\t{}\nReason:\n\t{}\n", f, toStr(openFileErr));
|
||||
continue;
|
||||
}
|
||||
m_activeEditor = m_editors.back().value->get();
|
||||
}
|
||||
} else {
|
||||
config.projects.emplace(0, StudioConfig::ProjectConfig{
|
||||
.projectPath = ox::String{m_project->projectPath()},
|
||||
.activeTabItemName = {},
|
||||
.openFiles = {},
|
||||
});
|
||||
}
|
||||
config.projects.resize(ox::min<size_t>(10, config.projects.size()));
|
||||
m_recentProjects.clear();
|
||||
m_recentProjects.reserve(config.projects.size());
|
||||
for (auto const &p : config.projects) {
|
||||
m_recentProjects.emplace_back(p.projectPath);
|
||||
}
|
||||
});
|
||||
return m_projectExplorer.refreshProjectTreeModel();
|
||||
}
|
||||
@@ -558,7 +705,10 @@ ox::Error StudioUI::openFile(ox::StringViewCR path) noexcept {
|
||||
|
||||
ox::Error StudioUI::openFileActiveTab(ox::StringViewCR path, bool const makeActiveTab) noexcept {
|
||||
if (!m_project) {
|
||||
return ox::Error(1, "No project open to open a file from");
|
||||
return ox::Error(1, "no project open to open a file from");
|
||||
}
|
||||
if (!m_project->romFs().exists(path)) {
|
||||
return ox::Error{1, "file does note exist"};
|
||||
}
|
||||
if (m_openFiles.contains(path)) {
|
||||
for (auto &e : m_editors) {
|
||||
@@ -572,10 +722,10 @@ ox::Error StudioUI::openFileActiveTab(ox::StringViewCR path, bool const makeActi
|
||||
}
|
||||
OX_REQUIRE(ext, fileExt(path));
|
||||
// create Editor
|
||||
BaseEditor *editor = nullptr;
|
||||
ox::UPtr<BaseEditor> editor;
|
||||
auto const err = m_editorMakers.contains(ext) ?
|
||||
m_editorMakers[ext](path).moveTo(editor) :
|
||||
ox::makeCatch<ClawEditor>(m_sctx, path).moveTo(editor);
|
||||
m_editorMakers[ext](path).to<ox::UPtr<BaseEditor>>().moveTo(editor) :
|
||||
ox::make_unique_catch<ClawEditor>(m_sctx, path).moveTo(editor);
|
||||
if (err) {
|
||||
if constexpr(!ox::defines::Debug) {
|
||||
oxErrf("Could not open Editor: {}\n", toStr(err));
|
||||
@@ -585,16 +735,18 @@ ox::Error StudioUI::openFileActiveTab(ox::StringViewCR path, bool const makeActi
|
||||
return err;
|
||||
}
|
||||
editor->closed.connect(this, &StudioUI::closeFile);
|
||||
m_editors.emplace_back(editor);
|
||||
auto const &e = m_editors.emplace_back(std::move(editor));
|
||||
m_openFiles.emplace_back(path);
|
||||
if (makeActiveTab) {
|
||||
m_activeEditor = m_editors.back().value->get();
|
||||
m_activeEditorUpdatePending = editor;
|
||||
m_activeEditorUpdatePending = e.get();
|
||||
}
|
||||
// save to config
|
||||
studio::editConfig<StudioConfig>(keelCtx(m_tctx), [&path](StudioConfig &config) {
|
||||
if (!config.openFiles.contains(path)) {
|
||||
config.openFiles.emplace_back(path);
|
||||
if (auto const pc = config.project()) {
|
||||
if (!pc->openFiles.contains(path)) {
|
||||
pc->openFiles.emplace_back(path);
|
||||
}
|
||||
}
|
||||
});
|
||||
return {};
|
||||
@@ -618,6 +770,22 @@ ox::Error StudioUI::handleCloseFileResponse(ig::PopupResponse const response) no
|
||||
return {};
|
||||
}
|
||||
|
||||
ox::Error StudioUI::handleRemoveRecentProjectResponse(ig::PopupResponse const response) noexcept {
|
||||
if (response == ig::PopupResponse::OK) {
|
||||
auto const p = std::move(m_recentProjects[m_removeRecentProject.idx]);
|
||||
studio::editConfig<StudioConfig>(keelCtx(m_tctx), [&p](StudioConfig &config) {
|
||||
std::ignore = config.projects.erase(
|
||||
std::remove_if(
|
||||
config.projects.begin(), config.projects.end(),
|
||||
[&p](StudioConfig::ProjectConfig const &pc) {
|
||||
return pc.projectPath == p;
|
||||
}));
|
||||
});
|
||||
return m_recentProjects.erase(m_removeRecentProject.idx).error;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
ox::Error StudioUI::closeCurrentFile() noexcept {
|
||||
for (auto &e : m_editors) {
|
||||
if (m_activeEditor == e.get()) {
|
||||
@@ -636,7 +804,9 @@ ox::Error StudioUI::closeFile(ox::StringViewCR path) noexcept {
|
||||
std::ignore = m_openFiles.erase(std::remove(m_openFiles.begin(), m_openFiles.end(), path));
|
||||
// save to config
|
||||
studio::editConfig<StudioConfig>(keelCtx(m_tctx), [&](StudioConfig &config) {
|
||||
std::ignore = config.openFiles.erase(std::remove(config.openFiles.begin(), config.openFiles.end(), path));
|
||||
if (auto const pc = config.project()) {
|
||||
std::ignore = pc->openFiles.erase(std::remove(pc->openFiles.begin(), pc->openFiles.end(), path));
|
||||
}
|
||||
});
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -14,23 +14,23 @@
|
||||
#include <studio/project.hpp>
|
||||
#include <studio/task.hpp>
|
||||
|
||||
#include "aboutpopup.hpp"
|
||||
#include "deleteconfirmation.hpp"
|
||||
#include "makecopypopup.hpp"
|
||||
#include "newdir.hpp"
|
||||
#include "newmenu.hpp"
|
||||
#include "newproject.hpp"
|
||||
#include "popups/about.hpp"
|
||||
#include "popups/deleteconfirmation.hpp"
|
||||
#include "popups/makecopy.hpp"
|
||||
#include "popups/newdir.hpp"
|
||||
#include "popups/newmenu.hpp"
|
||||
#include "popups/newproject.hpp"
|
||||
#include "projectexplorer.hpp"
|
||||
#include "renamefile.hpp"
|
||||
#include "popups/renamefile.hpp"
|
||||
|
||||
namespace studio {
|
||||
|
||||
class StudioUI: public ox::SignalHandler {
|
||||
class StudioUI final: public ox::SignalHandler {
|
||||
friend class StudioUIDrawer;
|
||||
|
||||
private:
|
||||
Context m_sctx;
|
||||
turbine::Context &m_tctx;
|
||||
turbine::Context &m_tctx{m_sctx.tctx};
|
||||
ox::String m_projectDataDir;
|
||||
ox::UPtr<Project> m_project;
|
||||
TaskRunner m_taskRunner;
|
||||
@@ -44,6 +44,7 @@ class StudioUI: public ox::SignalHandler {
|
||||
bool m_closeActiveTab{};
|
||||
ox::Vector<ox::Pair<ox::String>> m_queuedMoves;
|
||||
ox::Vector<ox::Pair<ox::String>> m_queuedDirMoves;
|
||||
ox::Vector<ox::String> m_recentProjects;
|
||||
NewMenu m_newMenu{keelCtx(m_tctx)};
|
||||
AboutPopup m_aboutPopup{m_tctx};
|
||||
DeleteConfirmation m_deleteConfirmation;
|
||||
@@ -57,7 +58,11 @@ class StudioUI: public ox::SignalHandler {
|
||||
MakeCopyPopup m_copyFilePopup;
|
||||
RenameFile m_renameFile;
|
||||
NewProject m_newProject{m_projectDataDir};
|
||||
ox::Array<Widget*, 10> const m_widgets {
|
||||
struct {
|
||||
ig::QuestionPopup dlg{"Remove From Recents?", "Unable to load project. Remove from recent projects?"};
|
||||
size_t idx{};
|
||||
} m_removeRecentProject;
|
||||
ox::Array<Widget*, 11> const m_widgets {
|
||||
&m_closeFileConfirm,
|
||||
&m_closeAppConfirm,
|
||||
&m_copyFilePopup,
|
||||
@@ -68,6 +73,7 @@ class StudioUI: public ox::SignalHandler {
|
||||
&m_newDirDialog,
|
||||
&m_renameFile,
|
||||
&m_messagePopup,
|
||||
&m_removeRecentProject.dlg,
|
||||
};
|
||||
bool m_showProjectExplorer = true;
|
||||
struct NavAction {
|
||||
@@ -77,11 +83,13 @@ class StudioUI: public ox::SignalHandler {
|
||||
ox::Optional<NavAction> m_navAction;
|
||||
|
||||
public:
|
||||
explicit StudioUI(turbine::Context &ctx, ox::StringParam projectDataDir) noexcept;
|
||||
explicit StudioUI(turbine::Context &tctx, ox::StringParam projectDataDir) noexcept;
|
||||
|
||||
void handleKeyEvent(turbine::Key, bool down) noexcept;
|
||||
|
||||
void navigateTo(ox::StringParam path, ox::StringParam navArgs) noexcept;
|
||||
void handleMouseButtonEvent(int btn, bool down) noexcept;
|
||||
|
||||
void handleNavigationChange(ox::StringParam path, ox::StringParam navArgs) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr Project *project() noexcept {
|
||||
@@ -100,9 +108,9 @@ class StudioUI: public ox::SignalHandler {
|
||||
|
||||
void drawTabs() noexcept;
|
||||
|
||||
void loadEditorMaker(EditorMaker const&editorMaker) noexcept;
|
||||
void loadEditorMaker(EditorMaker const &editorMaker) noexcept;
|
||||
|
||||
void loadModule(Module const&mod) noexcept;
|
||||
void loadModule(Module const &mod) noexcept;
|
||||
|
||||
void loadModules() noexcept;
|
||||
|
||||
@@ -124,7 +132,7 @@ class StudioUI: public ox::SignalHandler {
|
||||
|
||||
ox::Error renameFile(ox::StringViewCR path) noexcept;
|
||||
|
||||
ox::Error handleMoveFile(ox::StringViewCR oldPath, ox::StringViewCR newPath, ox::UUID const&id) noexcept;
|
||||
ox::Error handleMoveFile(ox::StringViewCR oldPath, ox::StringViewCR newPath, ox::UUID const &id) noexcept;
|
||||
|
||||
ox::Error handleDeleteDir(ox::StringViewCR path) noexcept;
|
||||
|
||||
@@ -144,6 +152,8 @@ class StudioUI: public ox::SignalHandler {
|
||||
|
||||
ox::Error handleCloseFileResponse(ig::PopupResponse response) noexcept;
|
||||
|
||||
ox::Error handleRemoveRecentProjectResponse(ig::PopupResponse response) noexcept;
|
||||
|
||||
ox::Error closeCurrentFile() noexcept;
|
||||
|
||||
ox::Error closeFile(ox::StringViewCR path) noexcept;
|
||||
|
||||
@@ -4,21 +4,21 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/claw/claw.hpp>
|
||||
#include <ox/fs/fs.hpp>
|
||||
#include <ox/model/typenamecatcher.hpp>
|
||||
#include <ox/oc/oc.hpp>
|
||||
#include <ox/std/buffer.hpp>
|
||||
#include <ox/std/defines.hpp>
|
||||
#include <ox/std/fmt.hpp>
|
||||
#include <ox/std/trace.hpp>
|
||||
#include <ox/std/string.hpp>
|
||||
#include <ox/std/trace.hpp>
|
||||
|
||||
#include <keel/context.hpp>
|
||||
|
||||
namespace studio {
|
||||
|
||||
namespace detail {
|
||||
inline ox::String slashesToPct(ox::StringView str) noexcept {
|
||||
inline ox::String slashesToPct(ox::StringViewCR str) noexcept {
|
||||
auto out = ox::String{str};
|
||||
for (auto&c: out) {
|
||||
if (c == '/' || c == '\\') {
|
||||
@@ -30,78 +30,102 @@ inline ox::String slashesToPct(ox::StringView str) noexcept {
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
ox::String configPath(keel::Context const&ctx) noexcept;
|
||||
ox::String configPath(keel::Context const &kctx) noexcept;
|
||||
|
||||
template<typename T>
|
||||
ox::Result<T> readConfig(keel::Context &ctx, ox::StringViewCR name) noexcept {
|
||||
ox::Result<T> readConfig(keel::Context &kctx, ox::StringViewCR name) noexcept {
|
||||
oxAssert(name != "", "Config type has no TypeName");
|
||||
auto const path = ox::sfmt("/{}.json", detail::slashesToPct(name));
|
||||
ox::PassThroughFS fs(configPath(ctx));
|
||||
ox::PassThroughFS fs(configPath(kctx));
|
||||
auto const [buff, err] = fs.read(path);
|
||||
if (err) {
|
||||
//oxErrf("Could not read config file: {} - {}\n", path, toStr(err));
|
||||
return err;
|
||||
}
|
||||
return ox::readOC<T>(buff);
|
||||
OX_REQUIRE(id, ox::readClawTypeId(buff));
|
||||
ox::Result<T> out;
|
||||
if (id != ox::ModelTypeId_v<T>) {
|
||||
out = keel::convert<T>(kctx, buff);
|
||||
} else {
|
||||
out = ox::readClaw<T>(buff);
|
||||
}
|
||||
OX_RETURN_ERROR(out);
|
||||
OX_RETURN_ERROR(keel::ensureValid(out.value));
|
||||
return out;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ox::Result<T> readConfig(keel::Context &ctx) noexcept {
|
||||
ox::Result<T> readConfig(keel::Context &kctx) noexcept {
|
||||
constexpr auto TypeName = ox::requireModelTypeName<T>();
|
||||
return readConfig<T>(ctx, TypeName);
|
||||
return readConfig<T>(kctx, TypeName);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ox::Error writeConfig(keel::Context &ctx, ox::StringViewCR name, T const&data) noexcept {
|
||||
ox::Error writeConfig(keel::Context &kctx, ox::StringViewCR name, T const &data) noexcept {
|
||||
oxAssert(name != "", "Config type has no TypeName");
|
||||
auto const path = ox::sfmt("/{}.json", detail::slashesToPct(name));
|
||||
ox::PassThroughFS fs(configPath(ctx));
|
||||
ox::PassThroughFS fs(configPath(kctx));
|
||||
if (auto const err = fs.mkdir("/", true)) {
|
||||
//oxErrf("Could not create config directory: {} - {}\n", path, toStr(err));
|
||||
return err;
|
||||
}
|
||||
OX_REQUIRE_M(buff, ox::writeOC(data));
|
||||
OX_REQUIRE_M(buff, ox::writeClaw(data, ox::ClawFormat::Organic));
|
||||
*buff.back().value = '\n';
|
||||
if (auto const err = fs.write(path, buff.data(), buff.size())) {
|
||||
//oxErrf("Could not read config file: {} - {}\n", path, toStr(err));
|
||||
return ox::Error(2, "Could not read config file");
|
||||
return ox::Error{2, "Could not read config file"};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ox::Error writeConfig(keel::Context &ctx, T const&data) noexcept {
|
||||
ox::Error writeConfig(keel::Context &kctx, T const &data) noexcept {
|
||||
constexpr auto TypeName = ox::requireModelTypeName<T>();
|
||||
return writeConfig(ctx, TypeName, data);
|
||||
return writeConfig(kctx, TypeName, data);
|
||||
}
|
||||
|
||||
template<typename T, typename Func>
|
||||
void openConfig(keel::Context &ctx, ox::StringViewCR name, Func f) noexcept {
|
||||
void openConfig(keel::Context &kctx, ox::StringViewCR name, Func f) noexcept {
|
||||
oxAssert(name != "", "Config type has no TypeName");
|
||||
auto const [c, err] = readConfig<T>(ctx, name);
|
||||
auto const [c, err] = readConfig<T>(kctx, name);
|
||||
oxLogError(err);
|
||||
f(c);
|
||||
}
|
||||
|
||||
template<typename T, typename Func>
|
||||
void openConfig(keel::Context &ctx, Func f) noexcept {
|
||||
void openConfig(keel::Context &kctx, Func f) noexcept {
|
||||
constexpr auto TypeName = ox::requireModelTypeName<T>();
|
||||
openConfig<T>(ctx, TypeName, f);
|
||||
openConfig<T>(kctx, TypeName, f);
|
||||
}
|
||||
|
||||
template<typename T, typename Func>
|
||||
void editConfig(keel::Context &ctx, ox::StringViewCR name, Func f) noexcept {
|
||||
void editConfig(keel::Context &kctx, ox::StringViewCR name, Func f) noexcept {
|
||||
oxAssert(name != "", "Config type has no TypeName");
|
||||
auto [c, err] = readConfig<T>(ctx, name);
|
||||
auto [c, err] = readConfig<T>(kctx, name);
|
||||
oxLogError(err);
|
||||
f(c);
|
||||
oxLogError(writeConfig(ctx, name, c));
|
||||
oxLogError(writeConfig(kctx, name, c));
|
||||
}
|
||||
|
||||
template<typename T, typename Func>
|
||||
void editConfig(keel::Context &ctx, Func f) noexcept {
|
||||
void editConfig(keel::Context &kctx, Func f) noexcept {
|
||||
constexpr auto TypeName = ox::requireModelTypeName<T>();
|
||||
editConfig<T>(ctx, TypeName, f);
|
||||
editConfig<T>(kctx, TypeName, f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Older config files didn't use ClawHeaders, so they can't
|
||||
* use the normal conversion system.
|
||||
* Functions like this shouldn't be necessary moving forward.
|
||||
*/
|
||||
template<typename T>
|
||||
ox::Error headerizeConfigFile(keel::Context &kctx, ox::StringViewCR name = ox::ModelTypeName_v<T>) noexcept {
|
||||
auto const path = ox::sfmt("/{}.json", detail::slashesToPct(name));
|
||||
ox::PassThroughFS fs(configPath(kctx));
|
||||
OX_REQUIRE_M(buff, fs.read(path));
|
||||
OX_REQUIRE_M(cv1, ox::readOC<T>(buff));
|
||||
OX_RETURN_ERROR(ox::writeClaw(cv1, ox::ClawFormat::Organic).moveTo(buff));
|
||||
return fs.write(path, buff);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,11 +15,24 @@ namespace studio {
|
||||
class StudioUI;
|
||||
|
||||
struct Context {
|
||||
StudioUI &ui;
|
||||
Project *project = nullptr;
|
||||
turbine::Context &tctx;
|
||||
Context(StudioUI &pUi, turbine::Context &pTctx) noexcept:
|
||||
ui(pUi), tctx(pTctx) {}
|
||||
public:
|
||||
friend void navigateTo(Context &ctx, ox::StringParam filePath, ox::StringParam navArgs) noexcept;
|
||||
friend void navigateBack(Context &ctx) noexcept;
|
||||
friend void navigateForward(Context &ctx) noexcept;
|
||||
friend StudioUI;
|
||||
StudioUI &ui;
|
||||
Project *project = nullptr;
|
||||
turbine::Context &tctx;
|
||||
protected:
|
||||
struct NavPath {
|
||||
ox::String filePath;
|
||||
ox::String navArgs;
|
||||
};
|
||||
size_t navIdx{};
|
||||
ox::Vector<NavPath> navStack;
|
||||
std::function<void(ox::StringParam filePath, ox::StringParam navArgs)> navCallback;
|
||||
Context(StudioUI &pUi, turbine::Context &pTctx) noexcept:
|
||||
ui{pUi}, tctx{pTctx} {}
|
||||
};
|
||||
|
||||
[[nodiscard]]
|
||||
@@ -27,6 +40,15 @@ inline keel::Context &keelCtx(Context &ctx) noexcept {
|
||||
return keelCtx(ctx.tctx);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
inline keel::Context const &keelCtx(Context const &ctx) noexcept {
|
||||
return keelCtx(ctx.tctx);
|
||||
}
|
||||
|
||||
void navigateTo(Context &ctx, ox::StringParam filePath, ox::StringParam navArgs = "") noexcept;
|
||||
|
||||
void navigateBack(Context &ctx) noexcept;
|
||||
|
||||
void navigateForward(Context &ctx) noexcept;
|
||||
|
||||
}
|
||||
|
||||
@@ -13,7 +13,11 @@ class FilePickerPopup {
|
||||
|
||||
private:
|
||||
ox::String m_name;
|
||||
FileExplorer m_explorer;
|
||||
struct Explorer: public FileExplorer {
|
||||
mutable bool opened{};
|
||||
explicit Explorer(keel::Context &kctx);
|
||||
void fileOpened(ox::StringViewCR path) const noexcept override;
|
||||
} m_explorer;
|
||||
ox::Vector<ox::String> const m_fileExts;
|
||||
bool m_open{};
|
||||
|
||||
@@ -33,6 +37,10 @@ class FilePickerPopup {
|
||||
|
||||
ox::Optional<ox::String> draw(Context &ctx) noexcept;
|
||||
|
||||
private:
|
||||
[[nodiscard]]
|
||||
ox::Optional<ox::String> handlePick() noexcept;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -46,14 +46,14 @@ ox::Result<T> getDragDropPayload() noexcept {
|
||||
static_cast<size_t>(payload->DataSize)});
|
||||
}
|
||||
|
||||
ox::Error setDragDropPayload(ox::CStringViewCR name, auto const&obj) noexcept {
|
||||
ox::Error setDragDropPayload(ox::CStringViewCR name, auto const &obj) noexcept {
|
||||
OX_REQUIRE(buff, ox::writeClaw(obj, ox::ClawFormat::Metal));
|
||||
ImGui::SetDragDropPayload(name.c_str(), buff.data(), buff.size());
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ox::Error setDragDropPayload(T const&obj) noexcept {
|
||||
ox::Error setDragDropPayload(T const &obj) noexcept {
|
||||
OX_REQUIRE(buff, ox::writeClaw(obj, ox::ClawFormat::Metal));
|
||||
ImGui::SetDragDropPayload(ox::ModelTypeName_v<T>, buff.data(), buff.size());
|
||||
return {};
|
||||
@@ -77,7 +77,7 @@ class DragDropSource {
|
||||
}
|
||||
};
|
||||
|
||||
auto dragDropSource(auto const&cb, ImGuiDragDropFlags const flags = 0) noexcept {
|
||||
auto dragDropSource(auto const &cb, ImGuiDragDropFlags const flags = 0) noexcept {
|
||||
if constexpr(ox::is_same_v<decltype(cb()), ox::Error>) {
|
||||
if (ig::DragDropSource const tgt{flags}; tgt) [[unlikely]] {
|
||||
return cb();
|
||||
@@ -108,7 +108,7 @@ class DragDropTarget {
|
||||
}
|
||||
};
|
||||
|
||||
auto dragDropTarget(auto const&cb) noexcept {
|
||||
auto dragDropTarget(auto const &cb) noexcept {
|
||||
if constexpr(ox::is_same_v<decltype(cb()), ox::Error>) {
|
||||
if (ig::DragDropTarget const tgt; tgt) [[unlikely]] {
|
||||
return cb();
|
||||
@@ -124,7 +124,7 @@ auto dragDropTarget(auto const&cb) noexcept {
|
||||
|
||||
class ChildStackItem {
|
||||
public:
|
||||
explicit ChildStackItem(ox::CStringViewCR id, ImVec2 const&sz = {}) noexcept;
|
||||
explicit ChildStackItem(ox::CStringViewCR id, ImVec2 const &sz = {}) noexcept;
|
||||
~ChildStackItem() noexcept;
|
||||
};
|
||||
|
||||
@@ -146,7 +146,7 @@ class IndentStackItem {
|
||||
|
||||
void centerNextWindow(turbine::Context &ctx) noexcept;
|
||||
|
||||
bool PushButton(ox::CStringViewCR lbl, ImVec2 const&btnSz = BtnSz) noexcept;
|
||||
bool PushButton(ox::CStringViewCR lbl, ImVec2 const &btnSz = BtnSz) noexcept;
|
||||
|
||||
template<typename Str>
|
||||
struct TextInput {
|
||||
@@ -234,7 +234,7 @@ PopupResponse PopupControlsOk(
|
||||
ox::CStringViewCR ok);
|
||||
|
||||
[[nodiscard]]
|
||||
bool BeginPopup(turbine::Context &ctx, ox::CStringViewCR popupName, bool &show, ImVec2 const&sz = {285, 0});
|
||||
bool BeginPopup(turbine::Context &ctx, ox::CStringViewCR popupName, bool &show, ImVec2 const &sz = {285, 0});
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -266,7 +266,7 @@ bool ComboBox(ox::CStringView lbl, ox::Span<const ox::String> list, size_t &sele
|
||||
*/
|
||||
bool ComboBox(
|
||||
ox::CStringViewCR lbl,
|
||||
std::function<ox::CStringView(size_t)> const&f,
|
||||
std::function<ox::CStringView(size_t)> const &f,
|
||||
size_t strCnt,
|
||||
size_t &selectedIdx) noexcept;
|
||||
|
||||
@@ -278,10 +278,10 @@ bool FileComboBox(
|
||||
|
||||
bool ListBox(
|
||||
ox::CStringViewCR name,
|
||||
std::function<ox::CStringView(size_t)> const&f,
|
||||
std::function<ox::CStringView(size_t)> const &f,
|
||||
size_t strCnt,
|
||||
size_t &selIdx,
|
||||
ImVec2 const&sz = {0, 0}) noexcept;
|
||||
ImVec2 const &sz = {0, 0}) noexcept;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -290,7 +290,7 @@ bool ListBox(
|
||||
* @param selIdx
|
||||
* @return true if new value selected, false otherwise
|
||||
*/
|
||||
bool ListBox(ox::CStringViewCR name, ox::SpanView<ox::String> const&list, size_t &selIdx) noexcept;
|
||||
bool ListBox(ox::CStringViewCR name, ox::SpanView<ox::String> const &list, size_t &selIdx) noexcept;
|
||||
|
||||
class FilePicker {
|
||||
private:
|
||||
@@ -306,7 +306,7 @@ class FilePicker {
|
||||
studio::Context &sctx,
|
||||
ox::StringParam title,
|
||||
ox::StringParam fileExt,
|
||||
ImVec2 const&size = {}) noexcept;
|
||||
ImVec2 const &size = {}) noexcept;
|
||||
|
||||
void draw() noexcept;
|
||||
|
||||
@@ -348,6 +348,8 @@ class QuestionPopup: public Popup {
|
||||
|
||||
QuestionPopup(ox::StringParam title, ox::StringParam question) noexcept;
|
||||
|
||||
void setQuestion(ox::StringParam msg) noexcept;
|
||||
|
||||
void draw(Context &ctx) noexcept override;
|
||||
|
||||
};
|
||||
|
||||
@@ -109,7 +109,7 @@ class ItemMaker {
|
||||
return m_typeDisplayName;
|
||||
}
|
||||
|
||||
bool installTemplate(ox::UPtr<ItemTemplate> &tmpl) {
|
||||
bool installTemplate(ox::UPtr<ItemTemplate> &&tmpl) {
|
||||
if (typeName() == tmpl->typeName() &&
|
||||
typeVersion() <= tmpl->typeVersion()) {
|
||||
m_templates.emplace_back(std::move(tmpl));
|
||||
@@ -120,10 +120,6 @@ class ItemMaker {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool installTemplate(ox::UPtr<ItemTemplate> &&tmpl) {
|
||||
return installTemplate(tmpl);
|
||||
}
|
||||
|
||||
constexpr ox::Vector<ox::UPtr<ItemTemplate>> const&itemTemplates() const noexcept {
|
||||
return m_templates;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
add_library(
|
||||
Studio
|
||||
configio.cpp
|
||||
context.cpp
|
||||
editor.cpp
|
||||
filepickerpopup.cpp
|
||||
filetreemodel.cpp
|
||||
|
||||
@@ -23,7 +23,7 @@ constexpr auto ConfigDir = [] {
|
||||
}
|
||||
}();
|
||||
|
||||
ox::String configPath(keel::Context const&ctx) noexcept {
|
||||
ox::String configPath(keel::Context const &ctx) noexcept {
|
||||
auto const homeDir = std::getenv(ox::defines::OS == ox::OS::Windows ? "USERPROFILE" : "HOME");
|
||||
return ox::sfmt(ConfigDir, homeDir, ctx.appName);
|
||||
}
|
||||
|
||||
69
src/olympic/studio/modlib/src/context.cpp
Normal file
69
src/olympic/studio/modlib/src/context.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <studio/context.hpp>
|
||||
|
||||
namespace studio {
|
||||
|
||||
void navigateTo(Context &ctx, ox::StringParam filePath, ox::StringParam navArgs) noexcept {
|
||||
ox::String path = std::move(filePath);
|
||||
if (beginsWith(path, "uuid://")) {
|
||||
auto const [p, err] = keel::uuidUrlToPath(keelCtx(ctx), path);
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
path = p;
|
||||
}
|
||||
ctx.navStack.resize(ctx.navIdx + 1);
|
||||
ctx.navStack.emplace_back(ox::String{path}, ox::String{navArgs.view()});
|
||||
try {
|
||||
ctx.navCallback(std::move(path), std::move(navArgs));
|
||||
} catch (std::exception const &e) {
|
||||
oxErrf("Nav error: {}", e.what());
|
||||
oxAssert(false, "Nav error");
|
||||
}
|
||||
}
|
||||
|
||||
void navigateBack(Context &ctx) noexcept {
|
||||
if (!ctx.navIdx) {
|
||||
return;
|
||||
}
|
||||
--ctx.navIdx;
|
||||
while (ctx.navIdx < ctx.navStack.size() && ctx.navIdx) {
|
||||
auto const i = ctx.navIdx - 1;
|
||||
auto const &n = ctx.navStack[i];
|
||||
if (!ctx.project->exists(n.filePath)) {
|
||||
std::ignore = ctx.navStack.erase(i);
|
||||
--ctx.navIdx;
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
ctx.navCallback(n.filePath, n.navArgs);
|
||||
} catch (std::exception const &e) {
|
||||
oxAssert(ctx.navCallback != nullptr, "navCallback is null");
|
||||
oxErrf("navigateForward failed: {}", e.what());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void navigateForward(Context &ctx) noexcept {
|
||||
while (ctx.navIdx < ctx.navStack.size()) {
|
||||
auto const &n = ctx.navStack[ctx.navIdx];
|
||||
try {
|
||||
ctx.navCallback(n.filePath, n.navArgs);
|
||||
} catch (std::exception const &e) {
|
||||
oxAssert(ctx.navCallback != nullptr, "navCallback is null");
|
||||
oxErrf("navigateForward failed: {}", e.what());
|
||||
}
|
||||
if (!ctx.project->exists(n.filePath)) {
|
||||
std::ignore = ctx.navStack.erase(ctx.navIdx);
|
||||
continue;
|
||||
}
|
||||
++ctx.navIdx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -72,8 +72,10 @@ bool BaseEditor::exportable() const noexcept {
|
||||
}
|
||||
|
||||
void BaseEditor::setCutEnabled(bool v) {
|
||||
m_cutEnabled = v;
|
||||
cutEnabledChanged.emit(v);
|
||||
if (m_cutEnabled != v) {
|
||||
m_cutEnabled = v;
|
||||
cutEnabledChanged.emit(v);
|
||||
}
|
||||
}
|
||||
|
||||
bool BaseEditor::cutEnabled() const noexcept {
|
||||
@@ -81,8 +83,10 @@ bool BaseEditor::cutEnabled() const noexcept {
|
||||
}
|
||||
|
||||
void BaseEditor::setCopyEnabled(bool v) {
|
||||
m_copyEnabled = v;
|
||||
copyEnabledChanged.emit(v);
|
||||
if (m_copyEnabled != v) {
|
||||
m_copyEnabled = v;
|
||||
copyEnabledChanged.emit(v);
|
||||
}
|
||||
}
|
||||
|
||||
bool BaseEditor::copyEnabled() const noexcept {
|
||||
@@ -90,8 +94,10 @@ bool BaseEditor::copyEnabled() const noexcept {
|
||||
}
|
||||
|
||||
void BaseEditor::setPasteEnabled(bool v) {
|
||||
m_pasteEnabled = v;
|
||||
pasteEnabledChanged.emit(v);
|
||||
if (m_pasteEnabled != v) {
|
||||
m_pasteEnabled = v;
|
||||
pasteEnabledChanged.emit(v);
|
||||
}
|
||||
}
|
||||
|
||||
bool BaseEditor::pasteEnabled() const noexcept {
|
||||
|
||||
@@ -8,6 +8,15 @@
|
||||
|
||||
namespace studio {
|
||||
|
||||
FilePickerPopup::Explorer::Explorer(keel::Context &kctx):
|
||||
FileExplorer{kctx} {
|
||||
}
|
||||
|
||||
void FilePickerPopup::Explorer::fileOpened(ox::StringViewCR) const noexcept {
|
||||
opened = true;
|
||||
}
|
||||
|
||||
|
||||
FilePickerPopup::FilePickerPopup(
|
||||
ox::StringParam name,
|
||||
keel::Context &kctx,
|
||||
@@ -41,6 +50,7 @@ void FilePickerPopup::refresh() noexcept {
|
||||
void FilePickerPopup::open() noexcept {
|
||||
refresh();
|
||||
m_open = true;
|
||||
m_explorer.opened = false;
|
||||
}
|
||||
|
||||
void FilePickerPopup::close() noexcept {
|
||||
@@ -60,16 +70,22 @@ ox::Optional<ox::String> FilePickerPopup::draw(Context &ctx) noexcept {
|
||||
if (ig::BeginPopup(ctx.tctx, m_name, m_open, {380, 340})) {
|
||||
auto const vp = ImGui::GetContentRegionAvail();
|
||||
m_explorer.draw(ctx, {vp.x, vp.y - 30});
|
||||
if (ig::PopupControlsOkCancel(m_open) == ig::PopupResponse::OK) {
|
||||
auto p = m_explorer.selectedPath();
|
||||
if (p) {
|
||||
out.emplace(*p);
|
||||
}
|
||||
close();
|
||||
if (ig::PopupControlsOkCancel(m_open) == ig::PopupResponse::OK || m_explorer.opened) {
|
||||
out = handlePick();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
ox::Optional<ox::String> FilePickerPopup::handlePick() noexcept {
|
||||
ox::Optional<ox::String> out;
|
||||
auto p = m_explorer.selectedPath();
|
||||
if (p) {
|
||||
out.emplace(*p);
|
||||
}
|
||||
close();
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
namespace studio::ig {
|
||||
|
||||
ChildStackItem::ChildStackItem(ox::CStringViewCR id, ImVec2 const&sz) noexcept {
|
||||
ChildStackItem::ChildStackItem(ox::CStringViewCR id, ImVec2 const &sz) noexcept {
|
||||
ImGui::BeginChild(id.c_str(), sz);
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ void centerNextWindow(turbine::Context &ctx) noexcept {
|
||||
ImGui::SetNextWindowPos(ImVec2(screenW / mod, screenH / mod), ImGuiCond_Always, ImVec2(0.5f, 0.5f));
|
||||
}
|
||||
|
||||
bool PushButton(ox::CStringViewCR lbl, ImVec2 const&btnSz) noexcept {
|
||||
bool PushButton(ox::CStringViewCR lbl, ImVec2 const &btnSz) noexcept {
|
||||
return ImGui::Button(lbl.c_str(), btnSz);
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ PopupResponse PopupControlsOk(
|
||||
return out;
|
||||
}
|
||||
|
||||
bool BeginPopup(turbine::Context &ctx, ox::CStringViewCR popupName, bool &show, ImVec2 const&sz) {
|
||||
bool BeginPopup(turbine::Context &ctx, ox::CStringViewCR popupName, bool &show, ImVec2 const &sz) {
|
||||
constexpr auto modalFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
|
||||
centerNextWindow(ctx);
|
||||
ImGui::OpenPopup(popupName.c_str());
|
||||
@@ -149,7 +149,7 @@ bool ComboBox(
|
||||
|
||||
bool ComboBox(
|
||||
ox::CStringViewCR lbl,
|
||||
std::function<ox::CStringView(size_t)> const&f,
|
||||
std::function<ox::CStringView(size_t)> const &f,
|
||||
size_t strCnt,
|
||||
size_t &selectedIdx) noexcept {
|
||||
bool out{};
|
||||
@@ -172,16 +172,16 @@ bool FileComboBox(
|
||||
Context &sctx,
|
||||
ox::StringViewCR fileExt,
|
||||
size_t &selectedIdx) noexcept {
|
||||
auto const&list = sctx.project->fileList(fileExt);
|
||||
auto const &list = sctx.project->fileList(fileExt);
|
||||
return ComboBox(lbl, list, selectedIdx);
|
||||
}
|
||||
|
||||
bool ListBox(
|
||||
ox::CStringViewCR name,
|
||||
std::function<ox::CStringView(size_t)> const&f,
|
||||
std::function<ox::CStringView(size_t)> const &f,
|
||||
size_t const strCnt,
|
||||
size_t &selIdx,
|
||||
ImVec2 const&sz) noexcept {
|
||||
ImVec2 const &sz) noexcept {
|
||||
auto out = false;
|
||||
if (ImGui::BeginListBox(name.c_str(), sz)) {
|
||||
for (size_t i = 0; i < strCnt; ++i) {
|
||||
@@ -199,13 +199,13 @@ bool ListBox(
|
||||
return out;
|
||||
}
|
||||
|
||||
bool ListBox(ox::CStringViewCR name, ox::SpanView<ox::String> const&list, size_t &selIdx) noexcept {
|
||||
bool ListBox(ox::CStringViewCR name, ox::SpanView<ox::String> const &list, size_t &selIdx) noexcept {
|
||||
return ListBox(name, [list](size_t i) -> ox::CStringView {
|
||||
return list[i];
|
||||
}, list.size(), selIdx);
|
||||
}
|
||||
|
||||
bool ListBox(ox::CStringViewCR name, ox::SpanView<ox::CStringView> const&list, size_t &selIdx) noexcept {
|
||||
bool ListBox(ox::CStringViewCR name, ox::SpanView<ox::CStringView> const &list, size_t &selIdx) noexcept {
|
||||
return ListBox(name, [list](size_t i) -> ox::CStringView {
|
||||
return list[i];
|
||||
}, list.size(), selIdx);
|
||||
@@ -216,7 +216,7 @@ FilePicker::FilePicker(
|
||||
Context &sctx,
|
||||
ox::StringParam title,
|
||||
ox::StringParam fileExt,
|
||||
ImVec2 const&size) noexcept:
|
||||
ImVec2 const &size) noexcept:
|
||||
m_sctx(sctx),
|
||||
m_title(std::move(title)),
|
||||
m_fileExt(std::move(fileExt)),
|
||||
@@ -230,7 +230,7 @@ void FilePicker::draw() noexcept {
|
||||
auto constexpr popupSz = ImVec2{450.f, 0};
|
||||
IDStackItem const idStackItem(m_title);
|
||||
if (BeginPopup(m_sctx.tctx, m_title, m_show, popupSz)) {
|
||||
auto const&list = m_sctx.project->fileList(m_fileExt);
|
||||
auto const &list = m_sctx.project->fileList(m_fileExt);
|
||||
size_t selIdx{};
|
||||
ComboBox(m_title, list, selIdx);
|
||||
if (PopupControlsOkCancel(popupSz.x, m_show) == ig::PopupResponse::OK) {
|
||||
@@ -267,6 +267,10 @@ QuestionPopup::QuestionPopup(ox::StringParam title, ox::StringParam question) no
|
||||
m_question{std::move(question)} {
|
||||
}
|
||||
|
||||
void QuestionPopup::setQuestion(ox::StringParam msg) noexcept {
|
||||
m_question = std::move(msg);
|
||||
}
|
||||
|
||||
void QuestionPopup::draw(Context &ctx) noexcept {
|
||||
switch (m_stage) {
|
||||
case Stage::Closed:
|
||||
@@ -275,6 +279,9 @@ void QuestionPopup::draw(Context &ctx) noexcept {
|
||||
ImGui::OpenPopup(m_title.c_str());
|
||||
m_stage = Stage::Open;
|
||||
m_open = true;
|
||||
// require extended refresh in case new contents require resize
|
||||
// this can take around a second
|
||||
turbine::requireRefreshFor(ctx.tctx, 1000);
|
||||
[[fallthrough]];
|
||||
case Stage::Open:
|
||||
centerNextWindow(ctx.tctx);
|
||||
|
||||
@@ -101,8 +101,8 @@ ox::Result<ox::FileStat> Project::stat(ox::StringViewCR path) const noexcept {
|
||||
ox::Error Project::copyItem(ox::StringViewCR src, ox::StringViewCR dest) noexcept {
|
||||
OX_REQUIRE_M(buff, loadBuff(src));
|
||||
OX_REQUIRE(id, keel::regenerateUuidHeader(buff));
|
||||
OX_RETURN_ERROR(writeBuff(dest, buff));
|
||||
createUuidMapping(m_kctx, dest, id);
|
||||
OX_RETURN_ERROR(writeBuff(dest, ox::BufferView{buff} + keel::K1HdrSz));
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,19 @@ ox::Error setWindowBounds(Context &ctx, ox::Bounds const&bnds) noexcept;
|
||||
* @param ctx - Context
|
||||
* @param ms - milliseconds
|
||||
*/
|
||||
void setRefreshWithin(Context &ctx, int ms) noexcept;
|
||||
void requireRefreshWithin(Context &ctx, int ms) noexcept;
|
||||
|
||||
/**
|
||||
* Stimulates screen to draw for a period of a short period of
|
||||
* time (168 ms on GLFW implementation).
|
||||
* @param ctx - Context
|
||||
*/
|
||||
void requireRefresh(Context &ctx) noexcept;
|
||||
|
||||
/**
|
||||
* Stimulates screen to draw for a specified period of time.
|
||||
* @param ctx - Context
|
||||
*/
|
||||
void requireRefreshFor(Context &ctx, int ms) noexcept;
|
||||
|
||||
}
|
||||
|
||||
@@ -79,11 +79,14 @@ enum Key {
|
||||
};
|
||||
|
||||
using KeyEventHandler = void(*)(Context&, Key, bool);
|
||||
using MouseButtonEventHandler = void(*)(Context&, int btn, bool);
|
||||
|
||||
void setKeyEventHandler(Context &ctx, KeyEventHandler h) noexcept;
|
||||
|
||||
void setMouseButtonEventHandler(Context &ctx, MouseButtonEventHandler h) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
KeyEventHandler keyEventHandler(Context &ctx) noexcept;
|
||||
KeyEventHandler keyEventHandler(Context const &ctx) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
bool buttonDown(Context const&ctx, Key) noexcept;
|
||||
|
||||
@@ -133,7 +133,9 @@ void setKeyEventHandler(Context &ctx, KeyEventHandler h) noexcept {
|
||||
ctx.keyEventHandler = h;
|
||||
}
|
||||
|
||||
KeyEventHandler keyEventHandler(Context &ctx) noexcept {
|
||||
void setMouseButtonEventHandler(Context&, MouseButtonEventHandler) noexcept {}
|
||||
|
||||
KeyEventHandler keyEventHandler(Context const &ctx) noexcept {
|
||||
return ctx.keyEventHandler;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ class Context {
|
||||
UpdateHandler updateHandler = [](Context&) -> int {return -1;};
|
||||
keel::Context keelCtx;
|
||||
KeyEventHandler keyEventHandler = nullptr;
|
||||
MouseButtonEventHandler mouseButtonEventHandler = nullptr;
|
||||
ox::AnyPtr applicationData;
|
||||
|
||||
// GLFW impl data ////////////////////////////////////////////////////////
|
||||
|
||||
@@ -155,6 +155,10 @@ static ox::Result<IconData> toGlfwImgPixels(ox::SpanView<uint8_t> const &iconPng
|
||||
return out;
|
||||
}
|
||||
|
||||
static void setMandatoryRefreshPeriod(Context &ctx, TimeMs const newVal) {
|
||||
ctx.mandatoryRefreshPeriodEnd = ox::max(ctx.mandatoryRefreshPeriodEnd, newVal);
|
||||
}
|
||||
|
||||
ox::Error setWindowIcon(Context &ctx, ox::SpanView<ox::SpanView<uint8_t>> const &iconPngs) noexcept {
|
||||
if constexpr(ox::defines::OS != ox::OS::Darwin) {
|
||||
ox::Vector<IconData, 8> src;
|
||||
@@ -216,10 +220,19 @@ ox::Error setWindowBounds(Context &ctx, ox::Bounds const&bnds) noexcept {
|
||||
return {};
|
||||
}
|
||||
|
||||
void setRefreshWithin(Context &ctx, int ms) noexcept {
|
||||
void requireRefreshWithin(Context &ctx, int const ms) noexcept {
|
||||
ctx.refreshWithinMs = ox::min(ms, ctx.refreshWithinMs);
|
||||
}
|
||||
|
||||
void requireRefresh(Context &ctx) noexcept {
|
||||
setMandatoryRefreshPeriod(ctx, ticksMs(ctx) + config::MandatoryRefreshPeriod);
|
||||
}
|
||||
|
||||
void requireRefreshFor(Context &ctx, int ms) noexcept {
|
||||
ms = ox::max(0, ms);
|
||||
setMandatoryRefreshPeriod(ctx, ticksMs(ctx) + static_cast<TimeMs>(ms));
|
||||
}
|
||||
|
||||
static void draw(Context &ctx) noexcept {
|
||||
// draw start
|
||||
#if TURBINE_USE_IMGUI
|
||||
@@ -286,14 +299,19 @@ static void handleKeyPress(Context &ctx, int const key, bool const down) noexcep
|
||||
static void handleGlfwCursorPosEvent(GLFWwindow*, double, double) noexcept {
|
||||
}
|
||||
|
||||
static void handleGlfwMouseButtonEvent(GLFWwindow *window, int, int, int) noexcept {
|
||||
static void handleGlfwMouseButtonEvent(
|
||||
GLFWwindow *window,
|
||||
int const btn,
|
||||
int const action,
|
||||
int) noexcept {
|
||||
auto &ctx = *static_cast<Context*>(glfwGetWindowUserPointer(window));
|
||||
ctx.mandatoryRefreshPeriodEnd = ticksMs(ctx) + config::MandatoryRefreshPeriod;
|
||||
setMandatoryRefreshPeriod(ctx, ticksMs(ctx) + config::MandatoryRefreshPeriod);
|
||||
ctx.mouseButtonEventHandler(ctx, btn, action == 1);
|
||||
}
|
||||
|
||||
static void handleGlfwKeyEvent(GLFWwindow *window, int const key, int, int const action, int) noexcept {
|
||||
auto &ctx = *static_cast<Context*>(glfwGetWindowUserPointer(window));
|
||||
ctx.mandatoryRefreshPeriodEnd = ticksMs(ctx) + config::MandatoryRefreshPeriod;
|
||||
setMandatoryRefreshPeriod(ctx, ticksMs(ctx) + config::MandatoryRefreshPeriod);
|
||||
if (action == GLFW_PRESS) {
|
||||
handleKeyPress(ctx, key, true);
|
||||
} else if (action == GLFW_RELEASE) {
|
||||
@@ -303,7 +321,7 @@ static void handleGlfwKeyEvent(GLFWwindow *window, int const key, int, int const
|
||||
|
||||
static void handleGlfwWindowCloseEvent(GLFWwindow *window) noexcept {
|
||||
auto &ctx = *static_cast<Context*>(glfwGetWindowUserPointer(window));
|
||||
ctx.mandatoryRefreshPeriodEnd = ticksMs(ctx) + config::MandatoryRefreshPeriod;
|
||||
setMandatoryRefreshPeriod(ctx, ticksMs(ctx) + config::MandatoryRefreshPeriod);
|
||||
ctx.running = ctx.shutdownHandler ? !ctx.shutdownHandler(ctx) : false;
|
||||
glfwSetWindowShouldClose(window, !ctx.running);
|
||||
glfwPostEmptyEvent();
|
||||
@@ -316,7 +334,7 @@ ox::Result<ox::UPtr<Context>> init(
|
||||
using namespace std::chrono;
|
||||
ctx->startTime = static_cast<TimeMs>(
|
||||
duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count());
|
||||
ctx->mandatoryRefreshPeriodEnd = ticksMs(*ctx) + config::MandatoryRefreshPeriod;
|
||||
setMandatoryRefreshPeriod(*ctx, ticksMs(*ctx) + config::MandatoryRefreshPeriod);
|
||||
// init GLFW context
|
||||
glfwInit();
|
||||
glfwSetErrorCallback(handleGlfwError);
|
||||
@@ -326,7 +344,8 @@ ox::Result<ox::UPtr<Context>> init(
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
|
||||
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
|
||||
constexpr auto Scale = 5;
|
||||
ctx->window = glfwCreateWindow(240 * Scale, 160 * Scale, ctx->keelCtx.appName.c_str(), nullptr, nullptr);
|
||||
ctx->window = glfwCreateWindow(
|
||||
240 * Scale, 160 * Scale, ctx->keelCtx.appName.c_str(), nullptr, nullptr);
|
||||
//ctx.window = glfwCreateWindow(876, 743, ctx.keelCtx.appName.c_str(), nullptr, nullptr);
|
||||
if (ctx->window == nullptr) {
|
||||
return ox::Error(1, "Could not open GLFW window");
|
||||
@@ -389,14 +408,14 @@ ox::Error run(Context &ctx) noexcept {
|
||||
glfwPollEvents();
|
||||
auto const ticks = ticksMs(ctx);
|
||||
if (ctx.wakeupTime <= ticks) {
|
||||
auto const st = ctx.updateHandler(ctx);
|
||||
if (st >= 0) {
|
||||
ctx.wakeupTime = ticks + static_cast<unsigned>(st);
|
||||
sleepTime = static_cast<uint64_t>(st);
|
||||
} else {
|
||||
ctx.wakeupTime = ~uint64_t{0};
|
||||
sleepTime = ctx.wakeupTime - ticks;
|
||||
}
|
||||
auto const st = ctx.updateHandler(ctx);
|
||||
if (st >= 0) {
|
||||
ctx.wakeupTime = ticks + static_cast<unsigned>(st);
|
||||
sleepTime = static_cast<uint64_t>(st);
|
||||
} else {
|
||||
ctx.wakeupTime = ~uint64_t{0};
|
||||
sleepTime = ctx.wakeupTime - ticks;
|
||||
}
|
||||
} else {
|
||||
sleepTime = ctx.wakeupTime - ticks;
|
||||
}
|
||||
@@ -434,15 +453,19 @@ void setShutdownHandler(Context &ctx, ShutdownHandler const handler) noexcept {
|
||||
ctx.shutdownHandler = handler;
|
||||
}
|
||||
|
||||
void setUpdateHandler(Context &ctx, UpdateHandler h) noexcept {
|
||||
void setUpdateHandler(Context &ctx, UpdateHandler const h) noexcept {
|
||||
ctx.updateHandler = h;
|
||||
}
|
||||
|
||||
void setKeyEventHandler(Context &ctx, KeyEventHandler h) noexcept {
|
||||
void setKeyEventHandler(Context &ctx, KeyEventHandler const h) noexcept {
|
||||
ctx.keyEventHandler = h;
|
||||
}
|
||||
|
||||
KeyEventHandler keyEventHandler(Context &ctx) noexcept {
|
||||
void setMouseButtonEventHandler(Context &ctx, MouseButtonEventHandler const h) noexcept {
|
||||
ctx.mouseButtonEventHandler = h;
|
||||
}
|
||||
|
||||
KeyEventHandler keyEventHandler(Context const &ctx) noexcept {
|
||||
return ctx.keyEventHandler;
|
||||
}
|
||||
|
||||
|
||||
59
util/scripts/loc.py
Executable file
59
util/scripts/loc.py
Executable file
@@ -0,0 +1,59 @@
|
||||
#! /usr/bin/env python3
|
||||
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(description="Count and sort lines of code in selected files.")
|
||||
parser.add_argument(
|
||||
"--search-dirs", nargs="+", required=True,
|
||||
help="List of directories to search (recursively)."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--include-exts", nargs="+", required=True,
|
||||
help="List of file extensions to include (e.g. .cpp .hpp .py)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--exclude-paths", nargs="*", default=[],
|
||||
help="Full file paths to exclude from counting."
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
include_exts = tuple(args.include_exts)
|
||||
exclude_paths = {Path(p).resolve() for p in args.exclude_paths}
|
||||
files = []
|
||||
for base in args.search_dirs:
|
||||
path = Path(base)
|
||||
if path.is_dir():
|
||||
for file in path.rglob("*"):
|
||||
try:
|
||||
resolved_file = file.resolve()
|
||||
if (
|
||||
file.is_file()
|
||||
and file.name.endswith(include_exts)
|
||||
and resolved_file not in exclude_paths
|
||||
):
|
||||
files.append(str(resolved_file))
|
||||
except FileNotFoundError:
|
||||
continue
|
||||
line_counts = []
|
||||
total_lines = 0
|
||||
for f in files:
|
||||
try:
|
||||
with open(f, "r", encoding="utf-8", errors="ignore") as file:
|
||||
lines = sum(1 for _ in file)
|
||||
line_counts.append((lines, f))
|
||||
total_lines += lines
|
||||
except Exception as e:
|
||||
print(f"Failed to read {f}: {e}")
|
||||
line_counts.sort()
|
||||
for count, filename in line_counts:
|
||||
print(f"{count:>7} {filename}")
|
||||
print(f"{total_lines:>7} total")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user