Squashed 'deps/nostalgia/' changes from 791b7746..a3d6a58c

a3d6a58c [nostalgia/core/studio] Fix library cpp file ownership
e598e7fe [nostalgia,keel] Add ability to types Obj to Obj
ba9e720f [ox/model] Fix ModelTypeName_v to use requireModelTypeName
8e816a26 [nostalgia/core/studio] Cleanup, fix possible TileSheet fill tool failure
5b9929ab [keel] Add detail to preload logging
ceb54b3f [nostalgia/core/opengl] Cleanup
87644447 [nostalgia/core] Add clearCbb functions
ce9a0b1f [nostalgia/core/opengl] Cleanup memcpys
f7a468ea [ox/std] Add spancpy
861d177a [studio] Cleanup
3936756b [nostalgia/developer-handbook] Update error handling to reflect the enablement of exceptions for GBA build
3e78ec3f [studio] Cleanup
3c3d53b4 [studio] Ensure Editor tabs do first draw immediately, fix shift key being missed with tab shortcuts
151d7c57 [nostalgia/core/gba] Fix partial tilesheet loading overrun
4e4d8d2c [nostalgia/core/gba] Make panic use standard abort call
03d1fd28 [ox/std] Add and integrate standard abort call
6701decc [gbabuildcore] Enable exceptions
6cff5266 [teagba] Add symbols needed for enabling exceptions
dd50bd02 [studio] Remap toggle explorer keyboard shortcut, add Ctrl+1-0 mappings for jumping between tabs
55a16602 [nostalgia/core] Fix TileSheet validation/repair to ensure pixels gets cleared if there are subsheets
ed365dfe [studio] Fix new project menu to return an appropriately sized string for name
23a09e4a [nostalgia/core/studio] Fix SubSheet editor to return an appropriately sized string
b69e7ebb [nostalgia/core/studio/tilesheeteditor] Fix select all not to go beyond end
418d6e3f [nostalgia/core/studio] Fix crash that occurs when a non-leaf node subsheet is selected
c44d8678 [nostalgia/core/studio] Fix tile insert to correct input when inserting past the last tile
eb4cd710 [nostalgia/core/studio] Fix tile insert to work on last tile
d259770f Merge commit '4ea4a61d542777a270c4e2c283e0e986fc9eec9c'
80bad608 [keel] Fix reloadAsset
2bce9a2b [ox/std] Add non-const SmallMap::pairs

git-subtree-dir: deps/nostalgia
git-subtree-split: a3d6a58cc898f88434e8901aacb579c819fac3e6
This commit is contained in:
Gary Talent 2025-01-17 21:58:18 -06:00
parent 4ea4a61d54
commit 50b1ed33df
39 changed files with 558 additions and 274 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -80,13 +80,13 @@ struct Exception: public std::exception {
ox::CString msg = nullptr; ox::CString msg = nullptr;
ErrorCode errCode = 0; ErrorCode errCode = 0;
explicit inline Exception( explicit Exception(
ErrorCode const errCode, ErrorCode const errCode,
std::source_location const&src = std::source_location::current()) noexcept: std::source_location const&src = std::source_location::current()) noexcept:
src{src}, src{src},
errCode{errCode} {} errCode{errCode} {}
explicit inline Exception( explicit Exception(
ErrorCode const errCode, ErrorCode const errCode,
ox::CString msg, ox::CString msg,
std::source_location const&src = std::source_location::current()) noexcept: std::source_location const&src = std::source_location::current()) noexcept:
@ -94,7 +94,7 @@ struct Exception: public std::exception {
msg{msg}, msg{msg},
errCode{errCode} {} errCode{errCode} {}
explicit inline Exception(Error const&err) noexcept: explicit Exception(Error const&err) noexcept:
src{err.src}, src{err.src},
msg{err.msg ? err.msg : ""}, msg{err.msg ? err.msg : ""},
errCode{err.errCode} {} errCode{err.errCode} {}
@ -109,6 +109,7 @@ struct Exception: public std::exception {
} }
}; };
[[noreturn]]
void panic(char const*file, int line, char const*panicMsg, Error const&err) noexcept; void panic(char const*file, int line, char const*panicMsg, Error const&err) noexcept;
template<typename T> template<typename T>

View File

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

View File

@ -85,6 +85,11 @@ class SmallMap {
return m_pairs; return m_pairs;
} }
[[nodiscard]]
constexpr ox::Span<Pair> pairs() noexcept {
return m_pairs;
}
constexpr void clear(); constexpr void clear();
private: private:

View File

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

View File

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

View File

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

View File

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

View File

@ -142,6 +142,10 @@ ox::Error loadBgTileSheet(
unsigned cbb, unsigned cbb,
TileSheetSet const&set) noexcept; TileSheetSet const&set) noexcept;
void clearCbb(Context &ctx, unsigned cbb) noexcept;
void clearCbbs(Context &ctx) noexcept;
ox::Error loadBgTileSheet( ox::Error loadBgTileSheet(
Context &ctx, Context &ctx,
unsigned cbb, unsigned cbb,

View File

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

View File

@ -18,6 +18,23 @@
namespace nostalgia::core { namespace nostalgia::core {
struct SubSheetTemplate {
static constexpr auto TypeName = "net.drinkingtea.nostalgia.gfx.SubSheetTemplate";
static constexpr auto TypeVersion = 1;
ox::String name;
int32_t width{};
int32_t height{};
ox::Vector<SubSheetTemplate> subsheets;
};
OX_MODEL_BEGIN(SubSheetTemplate)
OX_MODEL_FIELD(name)
OX_MODEL_FIELD(width)
OX_MODEL_FIELD(height)
OX_MODEL_FIELD(subsheets)
OX_MODEL_END()
// Predecessor to TileSheet, kept for backward compatibility // Predecessor to TileSheet, kept for backward compatibility
struct TileSheetV1 { struct TileSheetV1 {
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.NostalgiaGraphic"; static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.NostalgiaGraphic";
@ -237,10 +254,12 @@ struct TileSheetV4 {
[[nodiscard]] [[nodiscard]]
constexpr bool valid(TileSheetV4::SubSheet const&ss, int bpp) noexcept { constexpr bool valid(TileSheetV4::SubSheet const&ss, int bpp) noexcept {
auto const bytes = static_cast<size_t>(ss.columns * ss.rows * PixelsPerTile) / (bpp == 4 ? 2 : 1); auto const bytes = static_cast<size_t>(ss.columns * ss.rows * PixelsPerTile) / (bpp == 4 ? 2 : 1);
return ox::all_of(ss.subsheets.begin(), ss.subsheets.end(), return
[bpp, bytes](TileSheetV4::SubSheet const&s) { (ss.pixels.empty() || ss.subsheets.empty()) &&
return bytes == s.pixels.size() && valid(s, bpp); ox::all_of(ss.subsheets.begin(), ss.subsheets.end(),
}); [bpp, bytes](TileSheetV4::SubSheet const&s) {
return bytes == s.pixels.size() && valid(s, bpp);
});
} }
[[nodiscard]] [[nodiscard]]
@ -249,8 +268,14 @@ constexpr bool valid(TileSheetV4 const&ts) noexcept {
} }
constexpr void repair(TileSheetV4::SubSheet &ss, int bpp) noexcept { constexpr void repair(TileSheetV4::SubSheet &ss, int bpp) noexcept {
auto const bytes = static_cast<size_t>(ss.columns * ss.rows * PixelsPerTile) / (bpp == 4 ? 2 : 1); if (ss.subsheets.empty()) {
ss.pixels.resize(bytes); auto const bytes = static_cast<size_t>(ss.columns * ss.rows * PixelsPerTile) / (bpp == 4 ? 2 : 1);
ss.pixels.resize(bytes);
} else {
ss.pixels.clear();
ss.columns = -1;
ss.rows = -1;
}
for (auto &s : ss.subsheets) { for (auto &s : ss.subsheets) {
repair(s, bpp); repair(s, bpp);
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -9,5 +9,9 @@ target_sources(
commands/renamepagecommand.cpp commands/renamepagecommand.cpp
commands/updatecolorcommand.cpp commands/updatecolorcommand.cpp
commands/updatecolorinfocommand.cpp commands/updatecolorinfocommand.cpp
)
target_sources(
NostalgiaCore-Studio-ImGui PRIVATE
paletteeditor-imgui.cpp paletteeditor-imgui.cpp
) )

View File

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

View File

@ -6,7 +6,7 @@
namespace nostalgia::core { namespace nostalgia::core {
core::InsertTilesCommand::InsertTilesCommand( InsertTilesCommand::InsertTilesCommand(
TileSheet &img, TileSheet &img,
TileSheet::SubSheetIdx idx, TileSheet::SubSheetIdx idx,
std::size_t tileIdx, std::size_t tileIdx,
@ -31,9 +31,11 @@ ox::Error InsertTilesCommand::redo() noexcept {
auto &s = getSubSheet(m_img, m_idx); auto &s = getSubSheet(m_img, m_idx);
auto &p = s.pixels; auto &p = s.pixels;
auto dstPos = m_insertPos + m_insertCnt; auto dstPos = m_insertPos + m_insertCnt;
auto const dst = &p[dstPos];
auto const src = &p[m_insertPos]; auto const src = &p[m_insertPos];
ox::memmove(dst, src, p.size() - dstPos); if (dstPos < p.size()) {
auto const dst = &p[dstPos];
ox::memmove(dst, src, p.size() - dstPos);
}
ox::memset(src, 0, m_insertCnt * sizeof(decltype(p[0]))); ox::memset(src, 0, m_insertCnt * sizeof(decltype(p[0])));
return {}; return {};
} }
@ -41,12 +43,14 @@ ox::Error InsertTilesCommand::redo() noexcept {
ox::Error InsertTilesCommand::undo() noexcept { ox::Error InsertTilesCommand::undo() noexcept {
auto &s = getSubSheet(m_img, m_idx); auto &s = getSubSheet(m_img, m_idx);
auto &p = s.pixels; auto &p = s.pixels;
auto const srcIdx = m_insertPos + m_insertCnt;
auto const src = &p[srcIdx];
auto const dst1 = &p[m_insertPos]; auto const dst1 = &p[m_insertPos];
auto const dst2 = &p[p.size() - m_insertCnt]; auto const dst2 = &p[p.size() - m_insertCnt];
auto const sz = p.size() - srcIdx; auto const srcIdx = m_insertPos + m_insertCnt;
ox::memmove(dst1, src, sz); if (srcIdx < p.size()) {
auto const sz = p.size() - srcIdx;
auto const src = &p[srcIdx];
ox::memmove(dst1, src, sz);
}
ox::memcpy(dst2, m_deletedPixels.data(), m_deletedPixels.size()); ox::memcpy(dst2, m_deletedPixels.data(), m_deletedPixels.size());
return {}; return {};
} }

View File

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

View File

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

View File

@ -188,7 +188,7 @@ void TileSheetEditorImGui::draw(studio::StudioContext&) noexcept {
if (ImGui::IsKeyDown(ImGuiKey_ModCtrl)) { if (ImGui::IsKeyDown(ImGuiKey_ModCtrl)) {
if (ImGui::IsKeyPressed(ImGuiKey_A)) { if (ImGui::IsKeyPressed(ImGuiKey_A)) {
auto const&img = m_model.activeSubSheet(); auto const&img = m_model.activeSubSheet();
m_model.setSelection({{}, {img.columns * TileWidth, img.rows * TileHeight}}); m_model.setSelection({{}, {img.columns * TileWidth - 1, img.rows * TileHeight - 1}});
} else if (ImGui::IsKeyPressed(ImGuiKey_G)) { } else if (ImGui::IsKeyPressed(ImGuiKey_G)) {
m_model.clearSelection(); m_model.clearSelection();
} }
@ -544,7 +544,7 @@ void TileSheetEditorImGui::SubSheetEditor::draw(turbine::Context &tctx) noexcept
auto const popupHeight = modSize ? 130.f : 85.f; auto const popupHeight = modSize ? 130.f : 85.f;
auto const popupSz = ImVec2{popupWidth, popupHeight}; auto const popupSz = ImVec2{popupWidth, popupHeight};
if (ig::BeginPopup(tctx, popupName, m_show, popupSz)) { if (ig::BeginPopup(tctx, popupName, m_show, popupSz)) {
ImGui::InputText("Name", m_name.data(), m_name.cap()); ig::InputText("Name", m_name);
if (modSize) { if (modSize) {
ImGui::InputInt("Columns", &m_cols); ImGui::InputInt("Columns", &m_cols);
ImGui::InputInt("Rows", &m_rows); ImGui::InputInt("Rows", &m_rows);

View File

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

View File

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

View File

@ -56,8 +56,10 @@ void TileSheetEditorView::scrollH(ox::Vec2 const&paneSz, float wheelh) noexcept
} }
void TileSheetEditorView::insertTile(ox::Vec2 const&paneSize, ox::Vec2 const&clickPos) noexcept { void TileSheetEditorView::insertTile(ox::Vec2 const&paneSize, ox::Vec2 const&clickPos) noexcept {
auto const pt = clickPoint(paneSize, clickPos); auto pt = clickPoint(paneSize, clickPos);
auto const&s = m_model.activeSubSheet(); auto const&s = m_model.activeSubSheet();
pt.x = ox::min(pt.x, s.columns * TileWidth - 1);
pt.y = ox::min(pt.y, s.rows * TileHeight - 1);
auto const tileIdx = ptToIdx(pt, s.columns) / PixelsPerTile; auto const tileIdx = ptToIdx(pt, s.columns) / PixelsPerTile;
m_model.insertTiles(m_model.activeSubSheetIdx(), tileIdx, 1); m_model.insertTiles(m_model.activeSubSheetIdx(), tileIdx, 1);
} }

View File

@ -80,6 +80,11 @@ void TileSheetGrid::setBufferObject(
} }
void TileSheetGrid::setBufferObjects(ox::Vec2 const&paneSize, TileSheet::SubSheet const&subsheet) noexcept { void TileSheetGrid::setBufferObjects(ox::Vec2 const&paneSize, TileSheet::SubSheet const&subsheet) noexcept {
if (subsheet.columns < 1 || subsheet.rows < 1) {
m_bufferSet.elements.clear();
m_bufferSet.vertices.clear();
return;
}
auto const pixSize = pixelSize(paneSize); auto const pixSize = pixelSize(paneSize);
auto const set = [&](std::size_t i, ox::Point pt1, ox::Point pt2, Color32 c) { auto const set = [&](std::size_t i, ox::Point pt1, ox::Point pt2, Color32 c) {
auto const vbo = &m_bufferSet.vertices[i * VertexVboLength]; auto const vbo = &m_bufferSet.vertices[i * VertexVboLength];

View File

@ -51,7 +51,7 @@ ox::Error TileSheetPixels::buildShader() noexcept {
return glutils::buildShaderProgram(s_programSrc).moveTo(m_shader); return glutils::buildShaderProgram(s_programSrc).moveTo(m_shader);
} }
void TileSheetPixels::draw(bool update, ox::Vec2 const&scroll) noexcept { void TileSheetPixels::draw(bool const update, ox::Vec2 const&scroll) noexcept {
glUseProgram(m_shader); glUseProgram(m_shader);
glBindVertexArray(m_bufferSet.vao); glBindVertexArray(m_bufferSet.vao);
if (update) { if (update) {
@ -117,6 +117,11 @@ void TileSheetPixels::setPixelBufferObject(
void TileSheetPixels::setBufferObjects(ox::Vec2 const&paneSize) noexcept { void TileSheetPixels::setBufferObjects(ox::Vec2 const&paneSize) noexcept {
// set buffer lengths // set buffer lengths
auto const&subSheet = m_model.activeSubSheet(); auto const&subSheet = m_model.activeSubSheet();
if (subSheet.columns < 1 || subSheet.rows < 1) {
m_bufferSet.vertices.clear();
m_bufferSet.elements.clear();
return;
}
auto const&pal = m_model.pal(); auto const&pal = m_model.pal();
auto const width = subSheet.columns * TileWidth; auto const width = subSheet.columns * TileWidth;
auto const height = subSheet.rows * TileHeight; auto const height = subSheet.rows * TileHeight;

View File

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

View File

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

View File

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

View File

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

View File

@ -154,13 +154,12 @@ ox::Result<ox::CStringView> uuidToPath(Context &ctx, ox::UUID const&uuid) noexce
} }
ox::Error reloadAsset(keel::Context &ctx, ox::StringViewCR assetId) noexcept { ox::Error reloadAsset(keel::Context &ctx, ox::StringViewCR assetId) noexcept {
ox::UUIDStr uuidStr;
if (beginsWith(assetId, "uuid://")) { if (beginsWith(assetId, "uuid://")) {
return ctx.assetManager.reloadAsset(substr(assetId, 7)); return ctx.assetManager.reloadAsset(substr(assetId, 7));
} else { } else {
auto const [uuid, uuidErr] = getUuid(ctx, assetId); auto const [uuid, uuidErr] = getUuid(ctx, assetId);
if (!uuidErr) { if (!uuidErr) {
return ctx.assetManager.reloadAsset(uuidStr); return ctx.assetManager.reloadAsset(uuid.toString());
} else { } else {
return ctx.assetManager.reloadAsset(assetId); return ctx.assetManager.reloadAsset(assetId);
} }

View File

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

View File

@ -50,7 +50,7 @@ void NewProject::draw(studio::StudioContext &ctx) noexcept {
void NewProject::drawNewProjectName(studio::StudioContext &sctx) noexcept { void NewProject::drawNewProjectName(studio::StudioContext &sctx) noexcept {
drawWindow(sctx.tctx, &m_open, [this, &sctx] { drawWindow(sctx.tctx, &m_open, [this, &sctx] {
ImGui::InputText("Name", m_projectName.data(), m_projectName.cap()); ig::InputText("Name", m_projectName);
ImGui::Text("Path: %s", m_projectPath.c_str()); ImGui::Text("Path: %s", m_projectPath.c_str());
if (ImGui::Button("Browse")) { if (ImGui::Button("Browse")) {
oxLogError(studio::chooseDirectory().moveTo(m_projectPath)); oxLogError(studio::chooseDirectory().moveTo(m_projectPath));

View File

@ -18,9 +18,9 @@
namespace studio { namespace studio {
static ox::Vector<studio::Module const*> modules; static ox::Vector<Module const*> modules;
void registerModule(studio::Module const*mod) noexcept { void registerModule(Module const*mod) noexcept {
if (mod) { if (mod) {
modules.emplace_back(mod); modules.emplace_back(mod);
} }
@ -45,25 +45,25 @@ OX_MODEL_END()
StudioUI::StudioUI(turbine::Context &ctx, ox::StringParam projectDataDir) noexcept: StudioUI::StudioUI(turbine::Context &ctx, ox::StringParam projectDataDir) noexcept:
m_sctx(*this, ctx), m_sctx(*this, ctx),
m_ctx(ctx), m_tctx(ctx),
m_projectDataDir(std::move(projectDataDir)), m_projectDataDir(std::move(projectDataDir)),
m_projectExplorer(m_ctx), m_projectExplorer(m_tctx),
m_newProject(m_projectDataDir), m_newProject(m_projectDataDir),
m_aboutPopup(m_ctx) { m_aboutPopup(m_tctx) {
turbine::setApplicationData(m_ctx, &m_sctx); turbine::setApplicationData(m_tctx, &m_sctx);
m_projectExplorer.fileChosen.connect(this, &StudioUI::openFile); m_projectExplorer.fileChosen.connect(this, &StudioUI::openFile);
m_newProject.finished.connect(this, &StudioUI::createOpenProject); m_newProject.finished.connect(this, &StudioUI::createOpenProject);
m_newMenu.finished.connect(this, &StudioUI::openFile); m_newMenu.finished.connect(this, &StudioUI::openFile);
ImGui::GetIO().IniFilename = nullptr; ImGui::GetIO().IniFilename = nullptr;
loadModules(); loadModules();
// open project and files // open project and files
auto const [config, err] = studio::readConfig<StudioConfig>(keelCtx(m_ctx)); auto const [config, err] = studio::readConfig<StudioConfig>(keelCtx(m_tctx));
m_showProjectExplorer = config.showProjectExplorer; m_showProjectExplorer = config.showProjectExplorer;
if (!err) { if (!err) {
auto const openProjErr = openProjectPath(config.projectPath); auto const openProjErr = openProjectPath(config.projectPath);
if (!openProjErr) { if (!openProjErr) {
for (auto const&f: config.openFiles) { for (auto const&f: config.openFiles) {
auto openFileErr = openFileActiveTab(f, config.activeTabItemName == f); auto const openFileErr = openFileActiveTab(f, config.activeTabItemName == f);
if (openFileErr) { if (openFileErr) {
oxErrorf("\nCould not open editor for file:\n\t{}\nReason:\n\t{}\n", f, toStr(openFileErr)); oxErrorf("\nCould not open editor for file:\n\t{}\nReason:\n\t{}\n", f, toStr(openFileErr));
continue; continue;
@ -82,8 +82,8 @@ StudioUI::StudioUI(turbine::Context &ctx, ox::StringParam projectDataDir) noexce
} }
} }
void StudioUI::handleKeyEvent(turbine::Key key, bool down) noexcept { void StudioUI::handleKeyEvent(turbine::Key const key, bool const down) noexcept {
for (auto p : m_popups) { for (auto const p : m_popups) {
if (p->isOpen()) { if (p->isOpen()) {
if (key == turbine::Key::Escape) { if (key == turbine::Key::Escape) {
p->close(); p->close();
@ -118,13 +118,13 @@ void StudioUI::draw() noexcept {
for (auto &w: m_widgets) { for (auto &w: m_widgets) {
w->draw(m_sctx); w->draw(m_sctx);
} }
for (auto p: m_popups) { for (auto const p: m_popups) {
p->draw(m_sctx); p->draw(m_sctx);
} }
} }
ImGui::End(); ImGui::End();
handleKeyInput(); handleKeyInput();
m_taskRunner.update(m_ctx); m_taskRunner.update(m_tctx);
} }
void StudioUI::drawMenu() noexcept { void StudioUI::drawMenu() noexcept {
@ -143,7 +143,7 @@ void StudioUI::drawMenu() noexcept {
m_activeEditor->save(); m_activeEditor->save();
} }
if (ImGui::MenuItem("Quit", "Ctrl+Q")) { if (ImGui::MenuItem("Quit", "Ctrl+Q")) {
turbine::requestShutdown(m_ctx); turbine::requestShutdown(m_tctx);
} }
ImGui::EndMenu(); ImGui::EndMenu();
} }
@ -168,7 +168,7 @@ void StudioUI::drawMenu() noexcept {
ImGui::EndMenu(); ImGui::EndMenu();
} }
if (ImGui::BeginMenu("View")) { if (ImGui::BeginMenu("View")) {
if (ImGui::MenuItem("Project Explorer", "Ctrl+1", m_showProjectExplorer)) { if (ImGui::MenuItem("Project Explorer", "Ctrl+Shift+1", m_showProjectExplorer)) {
toggleProjectExplorer(); toggleProjectExplorer();
} }
ImGui::EndMenu(); ImGui::EndMenu();
@ -204,9 +204,10 @@ void StudioUI::drawTabs() noexcept {
if (ImGui::BeginTabItem(e->itemDisplayName().c_str(), &open, flags)) { if (ImGui::BeginTabItem(e->itemDisplayName().c_str(), &open, flags)) {
if (m_activeEditor != e.get()) [[unlikely]] { if (m_activeEditor != e.get()) [[unlikely]] {
m_activeEditor = e.get(); m_activeEditor = e.get();
studio::editConfig<StudioConfig>(keelCtx(m_ctx), [&](StudioConfig &config) { studio::editConfig<StudioConfig>(keelCtx(m_tctx), [&](StudioConfig &config) {
config.activeTabItemName = m_activeEditor->itemPath(); config.activeTabItemName = m_activeEditor->itemPath();
}); });
turbine::setRefreshWithin(m_tctx, 0);
} else [[likely]] { } else [[likely]] {
if (m_activeEditorUpdatePending == e.get()) [[unlikely]] { if (m_activeEditorUpdatePending == e.get()) [[unlikely]] {
m_activeEditorUpdatePending = nullptr; m_activeEditorUpdatePending = nullptr;
@ -237,13 +238,13 @@ void StudioUI::drawTabs() noexcept {
} }
} }
void StudioUI::loadEditorMaker(studio::EditorMaker const&editorMaker) noexcept { void StudioUI::loadEditorMaker(EditorMaker const&editorMaker) noexcept {
for (auto const&ext : editorMaker.fileTypes) { for (auto const&ext : editorMaker.fileTypes) {
m_editorMakers[ext] = editorMaker.make; m_editorMakers[ext] = editorMaker.make;
} }
} }
void StudioUI::loadModule(studio::Module const&mod) noexcept { void StudioUI::loadModule(Module const&mod) noexcept {
for (auto const&editorMaker : mod.editors(m_sctx)) { for (auto const&editorMaker : mod.editors(m_sctx)) {
loadEditorMaker(editorMaker); loadEditorMaker(editorMaker);
} }
@ -260,20 +261,20 @@ void StudioUI::loadModules() noexcept {
void StudioUI::toggleProjectExplorer() noexcept { void StudioUI::toggleProjectExplorer() noexcept {
m_showProjectExplorer = !m_showProjectExplorer; m_showProjectExplorer = !m_showProjectExplorer;
studio::editConfig<StudioConfig>(keelCtx(m_ctx), [&](StudioConfig &config) { editConfig<StudioConfig>(keelCtx(m_tctx), [&](StudioConfig &config) {
config.showProjectExplorer = m_showProjectExplorer; config.showProjectExplorer = m_showProjectExplorer;
}); });
} }
void StudioUI::redo() noexcept { void StudioUI::redo() noexcept {
auto undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr; auto const undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr;
if (undoStack && undoStack->canRedo()) { if (undoStack && undoStack->canRedo()) {
oxLogError(m_activeEditor->undoStack()->redo()); oxLogError(m_activeEditor->undoStack()->redo());
} }
} }
void StudioUI::undo() noexcept { void StudioUI::undo() noexcept {
auto undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr; auto const undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr;
if (undoStack && undoStack->canUndo()) { if (undoStack && undoStack->canUndo()) {
oxLogError(m_activeEditor->undoStack()->undo()); oxLogError(m_activeEditor->undoStack()->undo());
} }
@ -292,7 +293,7 @@ void StudioUI::handleKeyInput() noexcept {
m_activeEditor->copy(); m_activeEditor->copy();
} }
} else if (ImGui::IsKeyPressed(ImGuiKey_N)) { } else if (ImGui::IsKeyPressed(ImGuiKey_N)) {
if (turbine::buttonDown(m_ctx, turbine::Key::Mod_Shift)) { if (turbine::buttonDown(m_tctx, turbine::Key::Mod_Shift)) {
m_newProject.open(); m_newProject.open();
} else { } else {
m_newMenu.open(); m_newMenu.open();
@ -302,7 +303,7 @@ void StudioUI::handleKeyInput() noexcept {
} else if (ImGui::IsKeyPressed(ImGuiKey_S)) { } else if (ImGui::IsKeyPressed(ImGuiKey_S)) {
save(); save();
} else if (ImGui::IsKeyPressed(ImGuiKey_Q)) { } else if (ImGui::IsKeyPressed(ImGuiKey_Q)) {
turbine::requestShutdown(m_ctx); turbine::requestShutdown(m_tctx);
} else if (ImGui::IsKeyPressed(ImGuiKey_V)) { } else if (ImGui::IsKeyPressed(ImGuiKey_V)) {
if (m_activeEditor && m_activeEditor->pasteEnabled()) { if (m_activeEditor && m_activeEditor->pasteEnabled()) {
m_activeEditor->paste(); m_activeEditor->paste();
@ -317,8 +318,22 @@ void StudioUI::handleKeyInput() noexcept {
} else if (ImGui::IsKeyPressed(ImGuiKey_Z)) { } else if (ImGui::IsKeyPressed(ImGuiKey_Z)) {
auto const undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr; auto const undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr;
if (undoStack) { oxLogError(undoStack->undo()); } if (undoStack) { oxLogError(undoStack->undo()); }
} else if (ImGui::IsKeyPressed(ImGuiKey_1)) { } else if (ImGui::IsKeyPressed(ImGuiKey_1) && ImGui::IsKeyDown(ImGuiKey_ModShift)) {
toggleProjectExplorer(); toggleProjectExplorer();
} else {
if (!m_editors.empty()) {
auto const range = ox::min<size_t>(9u, m_editors.size()) - 1;
for (auto i = 0u; i < 9; ++i) {
if (ImGui::IsKeyPressed(static_cast<ImGuiKey>(static_cast<int>(ImGuiKey_1) + i))) {
m_activeEditor = m_editors[i < m_editors.size() ? i : range].get();
m_activeEditorUpdatePending = m_activeEditor;
}
}
if (ImGui::IsKeyPressed(ImGuiKey_0)) {
m_activeEditor = m_editors[10 < m_editors.size() ? 10 : range].get();
m_activeEditorUpdatePending = m_activeEditor;
}
}
} }
} }
} }
@ -333,18 +348,18 @@ ox::Error StudioUI::createOpenProject(ox::StringViewCR path) noexcept {
ox::Error StudioUI::openProjectPath(ox::StringParam path) noexcept { ox::Error StudioUI::openProjectPath(ox::StringParam path) noexcept {
OX_REQUIRE_M(fs, keel::loadRomFs(path.view())); OX_REQUIRE_M(fs, keel::loadRomFs(path.view()));
OX_RETURN_ERROR(keel::setRomFs(keelCtx(m_ctx), std::move(fs))); OX_RETURN_ERROR(keel::setRomFs(keelCtx(m_tctx), std::move(fs)));
OX_RETURN_ERROR( OX_RETURN_ERROR(
ox::make_unique_catch<studio::Project>(keelCtx(m_ctx), std::move(path), m_projectDataDir) ox::make_unique_catch<Project>(keelCtx(m_tctx), std::move(path), m_projectDataDir)
.moveTo(m_project)); .moveTo(m_project));
auto const sctx = applicationData<studio::StudioContext>(m_ctx); auto const sctx = applicationData<StudioContext>(m_tctx);
sctx->project = m_project.get(); sctx->project = m_project.get();
turbine::setWindowTitle(m_ctx, ox::sfmt("{} - {}", keelCtx(m_ctx).appName, m_project->projectPath())); turbine::setWindowTitle(m_tctx, ox::sfmt("{} - {}", keelCtx(m_tctx).appName, m_project->projectPath()));
m_project->fileAdded.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel); m_project->fileAdded.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel);
m_project->fileDeleted.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel); m_project->fileDeleted.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel);
m_openFiles.clear(); m_openFiles.clear();
m_editors.clear(); m_editors.clear();
studio::editConfig<StudioConfig>(keelCtx(m_ctx), [&](StudioConfig &config) { studio::editConfig<StudioConfig>(keelCtx(m_tctx), [&](StudioConfig &config) {
config.projectPath = ox::String(m_project->projectPath()); config.projectPath = ox::String(m_project->projectPath());
config.openFiles.clear(); config.openFiles.clear();
}); });
@ -355,7 +370,7 @@ ox::Error StudioUI::openFile(ox::StringViewCR path) noexcept {
return openFileActiveTab(path, true); return openFileActiveTab(path, true);
} }
ox::Error StudioUI::openFileActiveTab(ox::StringViewCR path, bool makeActiveTab) noexcept { ox::Error StudioUI::openFileActiveTab(ox::StringViewCR path, bool const makeActiveTab) noexcept {
if (!m_project) { if (!m_project) {
return ox::Error(1, "No project open to open a file from"); return ox::Error(1, "No project open to open a file from");
} }
@ -369,7 +384,7 @@ ox::Error StudioUI::openFileActiveTab(ox::StringViewCR path, bool makeActiveTab)
} }
return {}; return {};
} }
OX_REQUIRE(ext, studio::fileExt(path)); OX_REQUIRE(ext, fileExt(path));
// create Editor // create Editor
BaseEditor *editor = nullptr; BaseEditor *editor = nullptr;
auto const err = m_editorMakers.contains(ext) ? auto const err = m_editorMakers.contains(ext) ?
@ -391,7 +406,7 @@ ox::Error StudioUI::openFileActiveTab(ox::StringViewCR path, bool makeActiveTab)
m_activeEditorUpdatePending = editor; m_activeEditorUpdatePending = editor;
} }
// save to config // save to config
studio::editConfig<StudioConfig>(keelCtx(m_ctx), [&path](StudioConfig &config) { studio::editConfig<StudioConfig>(keelCtx(m_tctx), [&path](StudioConfig &config) {
if (!config.openFiles.contains(path)) { if (!config.openFiles.contains(path)) {
config.openFiles.emplace_back(path); config.openFiles.emplace_back(path);
} }
@ -405,7 +420,7 @@ ox::Error StudioUI::closeFile(ox::StringViewCR path) noexcept {
} }
std::ignore = m_openFiles.erase(std::remove(m_openFiles.begin(), m_openFiles.end(), path)); std::ignore = m_openFiles.erase(std::remove(m_openFiles.begin(), m_openFiles.end(), path));
// save to config // save to config
studio::editConfig<StudioConfig>(keelCtx(m_ctx), [&](StudioConfig &config) { studio::editConfig<StudioConfig>(keelCtx(m_tctx), [&](StudioConfig &config) {
std::ignore = config.openFiles.erase(std::remove(config.openFiles.begin(), config.openFiles.end(), path)); std::ignore = config.openFiles.erase(std::remove(config.openFiles.begin(), config.openFiles.end(), path));
}); });
return {}; return {};

View File

@ -24,23 +24,23 @@ class StudioUI: public ox::SignalHandler {
friend class StudioUIDrawer; friend class StudioUIDrawer;
private: private:
studio::StudioContext m_sctx; StudioContext m_sctx;
turbine::Context &m_ctx; turbine::Context &m_tctx;
ox::String m_projectDataDir; ox::String m_projectDataDir;
ox::UPtr<studio::Project> m_project; ox::UPtr<Project> m_project;
studio::TaskRunner m_taskRunner; TaskRunner m_taskRunner;
ox::Vector<ox::UPtr<studio::BaseEditor>> m_editors; ox::Vector<ox::UPtr<BaseEditor>> m_editors;
ox::Vector<ox::UPtr<studio::Widget>> m_widgets; ox::Vector<ox::UPtr<Widget>> m_widgets;
ox::HashMap<ox::String, studio::EditorMaker::Func> m_editorMakers; ox::HashMap<ox::String, EditorMaker::Func> m_editorMakers;
ProjectExplorer m_projectExplorer; ProjectExplorer m_projectExplorer;
ox::Vector<ox::String> m_openFiles; ox::Vector<ox::String> m_openFiles;
studio::BaseEditor *m_activeEditorOnLastDraw = nullptr; BaseEditor *m_activeEditorOnLastDraw = nullptr;
studio::BaseEditor *m_activeEditor = nullptr; BaseEditor *m_activeEditor = nullptr;
studio::BaseEditor *m_activeEditorUpdatePending = nullptr; BaseEditor *m_activeEditorUpdatePending = nullptr;
NewMenu m_newMenu; NewMenu m_newMenu;
NewProject m_newProject; NewProject m_newProject;
AboutPopup m_aboutPopup; AboutPopup m_aboutPopup;
ox::Array<studio::Popup*, 3> const m_popups = { ox::Array<Popup*, 3> const m_popups = {
&m_newMenu, &m_newMenu,
&m_newProject, &m_newProject,
&m_aboutPopup &m_aboutPopup
@ -53,7 +53,7 @@ class StudioUI: public ox::SignalHandler {
void handleKeyEvent(turbine::Key, bool down) noexcept; void handleKeyEvent(turbine::Key, bool down) noexcept;
[[nodiscard]] [[nodiscard]]
constexpr studio::Project *project() noexcept { constexpr Project *project() noexcept {
return m_project.get(); return m_project.get();
} }
@ -67,9 +67,9 @@ class StudioUI: public ox::SignalHandler {
void drawTabs() noexcept; void drawTabs() noexcept;
void loadEditorMaker(studio::EditorMaker const&editorMaker) noexcept; void loadEditorMaker(EditorMaker const&editorMaker) noexcept;
void loadModule(studio::Module const&mod) noexcept; void loadModule(Module const&mod) noexcept;
void loadModules() noexcept; void loadModules() noexcept;