Compare commits

...

44 Commits

Author SHA1 Message Date
13e256b3a9 [nostalgia/studio] Update version to d2025.02.0
All checks were successful
Build / build (push) Successful in 1m28s
2025-02-25 20:15:07 -06:00
6bd74611cd [nostalgia] Update release notes
All checks were successful
Build / build (push) Successful in 1m28s
2025-02-25 20:02:52 -06:00
c3f9cf9a64 [studio] Fix New Project opening project, disable New if no project open
Some checks failed
Build / build (push) Has been cancelled
2025-02-25 20:01:33 -06:00
646ab1283f [nostalgia/gfx] Cleanup
All checks were successful
Build / build (push) Successful in 1m26s
2025-02-24 21:45:45 -06:00
74cf055610 [nostalgia] Cleanup
All checks were successful
Build / build (push) Successful in 1m32s
2025-02-24 19:43:10 -06:00
0d8ba1b154 [nostalgia/gfx] Cleanup formatting mistake
All checks were successful
Build / build (push) Successful in 1m26s
2025-02-23 23:21:59 -06:00
20edbb7f38 [buildcore] Map aarch64 to arm64
All checks were successful
Build / build (push) Successful in 1m27s
2025-02-23 01:00:12 -06:00
6febc7cc73 [nostalgia] Fix build
All checks were successful
Build / build (push) Successful in 1m32s
2025-02-23 00:51:30 -06:00
b94d6b5061 [nostalgia] Remove scene package, finish stubbing out sound
Some checks failed
Build / build (push) Failing after 21s
2025-02-23 00:49:58 -06:00
b3952cabbc [nostalgia] Add build upload step to CI
All checks were successful
Build / build (push) Successful in 1m39s
2025-02-22 21:30:35 -06:00
2ffc11b04e Merge commit 'e723ead864edb4bc160e4d69713309174ad9e82e'
All checks were successful
Build / build (push) Successful in 1m34s
2025-02-22 20:55:17 -06:00
96cace2cbb [studio] Cleanup 2025-02-22 19:51:55 -06:00
472f5702bd [nostalgia/gfx/studio/tilesheet] Change max export scale to 135
All checks were successful
Build / build (push) Successful in 1m32s
2025-02-22 15:31:39 -06:00
c0ac4345d3 [studio] Cleanup
All checks were successful
Build / build (push) Successful in 1m30s
2025-02-22 15:16:26 -06:00
fbebf4ef83 [nostalgia/gfx/studio/tilesheet] Fix export for 4bpp images
All checks were successful
Build / build (push) Successful in 1m27s
2025-02-22 00:58:12 -06:00
20513f7749 [nostalgia/sample_project] Add type descriptors 2025-02-21 00:41:04 -06:00
25a7873ea2 [nostalgia,studio] Fix crash that occurred when navigating to file that is not already open
All checks were successful
Build / build (push) Successful in 1m32s
2025-02-20 23:57:02 -06:00
d0a32e247e [ox/std] Add Vector::remove
All checks were successful
Build / build (push) Successful in 1m42s
2025-02-20 23:34:36 -06:00
03d4a5736e [nostalgia,studio] Add ability to navigate from tile sheet to palette color
All checks were successful
Build / build (push) Successful in 1m52s
2025-02-20 23:30:50 -06:00
a2e41e6527 Merge commit '4e94c925686cdda4b1ac777045dd7a17c7dc0329'
All checks were successful
Build / build (push) Successful in 1m38s
2025-02-20 20:11:03 -06:00
40a7caff90 [ox/std] Make bounds checking its own option enable-able in release builds 2025-02-20 20:05:07 -06:00
26fc5565e8 [nostalgia/gfx] Make dangling reference warning suppressions check for GCC 13
All checks were successful
Build / build (push) Successful in 1m30s
2025-02-20 19:40:08 -06:00
388541ce32 [nostalgia/player] Cleanup
All checks were successful
Build / build (push) Successful in 1m24s
2025-02-20 00:01:29 -06:00
6c194667b9 [nostalgia] Fix NostalgiaGfx lib name, stub out sound package
All checks were successful
Build / build (push) Successful in 1m27s
2025-02-19 22:19:16 -06:00
62d0579f40 [ox/fs] Restructure stat error handling to make easier to debug
All checks were successful
Build / build (push) Successful in 1m25s
2025-02-19 21:47:47 -06:00
202595b2a6 [keel] Fix loading assets by path 2025-02-19 21:47:00 -06:00
cb21ff3f04 Merge commit 'a6b9657268eb3fe139b0c22df27c2cb2efc0013c' 2025-02-19 00:34:26 -06:00
2a8e3c2dc4 [nostalgia/gfx] Remove unnecessary cast
All checks were successful
Build / build (push) Successful in 1m26s
2025-02-18 23:01:15 -06:00
998066d377 [ox/std] Add comparison functions
All checks were successful
Build / build (push) Successful in 1m24s
2025-02-18 21:46:41 -06:00
fefb876fe7 [nostalgia/gfx] Add checks for GCC version for warning suppression
All checks were successful
Build / build (push) Successful in 1m24s
2025-02-18 20:33:29 -06:00
5979e9885e [jsoncpp] Up required CMake version 2025-02-18 20:26:47 -06:00
a17abe4639 [nfde] Up required CMake version 2025-02-18 20:26:47 -06:00
d62f913855 [nostalgia/gfx] Suppress some superfluous warnings
Some checks failed
Build / build (push) Failing after 1m9s
2025-02-18 20:22:56 -06:00
12bb7475fc [nostalgia/gfx/studio/tilesheet] Adjust pixel line size on Windows
All checks were successful
Build / build (push) Successful in 1m25s
2025-02-18 20:19:51 -06:00
df2c7e2b67 [nostalgia] Update release notes
All checks were successful
Build / build (push) Successful in 1m26s
2025-02-08 18:10:49 -06:00
713aec887b [buildcore] Change mypy invokation
All checks were successful
Build / build (push) Successful in 1m28s
2025-02-07 20:38:44 -06:00
3089cd7afc Change builder type to olympic
Some checks failed
Build / build (push) Has been cancelled
2025-02-07 20:34:22 -06:00
00638bc812 [nostalgia/gfx/studio/tilesheet] Mark DrawCommands as obsolete if no changes
All checks were successful
Build / build (push) Successful in 3m37s
2025-02-05 20:26:47 -06:00
e002109829 [studio] Make undo/redo skip over obsolete commands 2025-02-05 20:26:03 -06:00
b4798fd2ab [nostalgia/gfx/studio/tilesheet] Make rotate only available for square subsheets or selections
All checks were successful
Build / build (push) Successful in 3m39s
2025-02-05 01:54:41 -06:00
8459d3baea Merge commit 'c42adc290cd8a27d01bb6d9877032dd2c963a4b7' 2025-02-01 22:55:46 -06:00
8d04af691e Merge commit 'ab760b064fd6a302bad13274e0e02b2b2c957b67' 2025-01-26 15:42:50 -06:00
6c34198f58 Merge commit '897a59cdad66e593fd45eece9414d8414fa7f1ae' 2025-01-23 23:51:13 -06:00
f63c58169f [studio] Add filepickerpopup.hpp to studio.hpp 2025-01-23 22:19:59 -06:00
90 changed files with 724 additions and 948 deletions

View File

@ -4,7 +4,7 @@ on: [push]
jobs: jobs:
build: build:
runs-on: nostalgia runs-on: olympic
steps: steps:
- name: Check out repository code - name: Check out repository code
uses: actions/checkout@v3 uses: actions/checkout@v3
@ -17,3 +17,10 @@ jobs:
- run: make purge configure-release - run: make purge configure-release
- run: make build - run: make build
- run: make test - run: make test
- run: make install
- run: mv dist/linux-x86_64-release nostalgia-linux-x86_64
- run: tar cf nostalgia-linux-x86_64.tar nostalgia-linux-x86_64
- uses: actions/upload-artifact@v3
with:
name: nostalgia-linux-x86_64
path: nostalgia-linux-x86_64.tar

View File

@ -93,7 +93,7 @@ purge:
${BC_CMD_RM_RF} compile_commands.json ${BC_CMD_RM_RF} compile_commands.json
.PHONY: test .PHONY: test
test: build test: build
${BC_CMD_ENVRUN} mypy ${BC_VAR_SCRIPTS} ${BC_CMD_ENVRUN} ${BC_CMD_PY3} -m mypy ${BC_VAR_SCRIPTS}
${BC_CMD_CMAKE_BUILD} ${BC_VAR_BUILD_PATH} test ${BC_CMD_CMAKE_BUILD} ${BC_VAR_BUILD_PATH} test
.PHONY: test-verbose .PHONY: test-verbose
test-verbose: build test-verbose: build

View File

@ -35,4 +35,6 @@ def get_arch() -> str:
arch = platform.machine().lower() arch = platform.machine().lower()
if arch == 'amd64': if arch == 'amd64':
arch = 'x86_64' arch = 'x86_64'
elif arch == 'aarch64':
arch = 'arm64'
return arch return arch

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.19)
project(nativefiledialog-extended VERSION 1.1.1) project(nativefiledialog-extended VERSION 1.1.1)
set(nfd_ROOT_PROJECT OFF) set(nfd_ROOT_PROJECT OFF)

View File

@ -12,7 +12,7 @@
# CMake versions greater than the JSONCPP_NEWEST_VALIDATED_POLICIES_VERSION policies will # CMake versions greater than the JSONCPP_NEWEST_VALIDATED_POLICIES_VERSION policies will
# continue to generate policy warnings "CMake Warning (dev)...Policy CMP0XXX is not set:" # continue to generate policy warnings "CMake Warning (dev)...Policy CMP0XXX is not set:"
# #
set(JSONCPP_OLDEST_VALIDATED_POLICIES_VERSION "3.8.0") set(JSONCPP_OLDEST_VALIDATED_POLICIES_VERSION "3.13.2")
set(JSONCPP_NEWEST_VALIDATED_POLICIES_VERSION "3.13.2") set(JSONCPP_NEWEST_VALIDATED_POLICIES_VERSION "3.13.2")
cmake_minimum_required(VERSION ${JSONCPP_OLDEST_VALIDATED_POLICIES_VERSION}) cmake_minimum_required(VERSION ${JSONCPP_OLDEST_VALIDATED_POLICIES_VERSION})
if("${CMAKE_VERSION}" VERSION_LESS "${JSONCPP_NEWEST_VALIDATED_POLICIES_VERSION}") if("${CMAKE_VERSION}" VERSION_LESS "${JSONCPP_NEWEST_VALIDATED_POLICIES_VERSION}")

View File

@ -93,7 +93,9 @@ Result<FileStat> PassThroughFS::statPath(StringViewCR path) const noexcept {
oxTracef("ox.fs.PassThroughFS.statInode", "{} {}", ec.message(), path); oxTracef("ox.fs.PassThroughFS.statInode", "{} {}", ec.message(), path);
const uint64_t size = type == FileType::Directory ? 0 : std::filesystem::file_size(p, ec); const uint64_t size = type == FileType::Directory ? 0 : std::filesystem::file_size(p, ec);
oxTracef("ox.fs.PassThroughFS.statInode.size", "{} {}", path, size); oxTracef("ox.fs.PassThroughFS.statInode.size", "{} {}", path, size);
OX_RETURN_ERROR(ox::Error(static_cast<ox::ErrorCode>(ec.value()), "PassThroughFS: stat failed")); if (auto err = ec.value()) {
return ox::Error{static_cast<ox::ErrorCode>(err), "PassThroughFS: stat failed"};
}
return FileStat{0, 0, size, type}; return FileStat{0, 0, size, type};
} }

View File

@ -181,13 +181,13 @@ constexpr Array<T, ArraySize> &Array<T, ArraySize>::operator=(Array &&other) noe
template<typename T, std::size_t ArraySize> template<typename T, std::size_t ArraySize>
constexpr T &Array<T, ArraySize>::operator[](std::size_t i) noexcept { constexpr T &Array<T, ArraySize>::operator[](std::size_t i) noexcept {
ox::primitiveAssert(__FILE__, __LINE__, i < size(), "Array access overflow"); boundsCheck(__FILE__, __LINE__, i, size(), "Array access overflow");
return m_items[i]; return m_items[i];
} }
template<typename T, std::size_t ArraySize> template<typename T, std::size_t ArraySize>
constexpr const T &Array<T, ArraySize>::operator[](std::size_t i) const noexcept { constexpr const T &Array<T, ArraySize>::operator[](std::size_t i) const noexcept {
ox::primitiveAssert(__FILE__, __LINE__, i < size(), "Array access overflow"); boundsCheck(__FILE__, __LINE__, i, size(), "Array access overflow");
return m_items[i]; return m_items[i];
} }

View File

@ -41,6 +41,12 @@ constexpr auto Debug = true;
constexpr auto Debug = false; constexpr auto Debug = false;
#endif #endif
#if defined(OX_CHECK_BOUNDS)
constexpr auto CheckBounds = true;
#else
constexpr auto CheckBounds = Debug;
#endif
#if defined(NDEBUG) #if defined(NDEBUG)
constexpr auto NDebug = true; constexpr auto NDebug = true;
#else #else

View File

@ -330,4 +330,17 @@ constexpr void primitiveAssert(char const*file, int line, bool pass, char const*
} }
} }
constexpr void boundsCheck(
char const*file,
int const line,
size_t const i,
size_t const sz,
char const*msg) noexcept {
if constexpr(defines::CheckBounds) {
if (i >= sz) [[unlikely]] {
panic(file, line, msg, ox::Error{1});
}
}
}
} }

View File

@ -133,17 +133,17 @@ struct SpanIterator {
} }
constexpr PtrType operator->() const noexcept { constexpr PtrType operator->() const noexcept {
ox::primitiveAssert(__FILE__, __LINE__, m_offset < m_max, "SpanIterator access overflow"); boundsCheck(__FILE__, __LINE__, m_offset, m_max, "SpanIterator access overflow");
return &m_t[m_offset]; return &m_t[m_offset];
} }
constexpr RefType operator*() const noexcept { constexpr RefType operator*() const noexcept {
ox::primitiveAssert(__FILE__, __LINE__, m_offset < m_max, "SpanIterator access overflow"); boundsCheck(__FILE__, __LINE__, m_offset, m_max, "SpanIterator access overflow");
return m_t[m_offset]; return m_t[m_offset];
} }
constexpr RefType operator[](std::size_t s) const noexcept { constexpr RefType operator[](std::size_t s) const noexcept {
ox::primitiveAssert(__FILE__, __LINE__, s < m_max, "SpanIterator access overflow"); boundsCheck(__FILE__, __LINE__, s, m_max, "SpanIterator access overflow");
return m_t[s]; return m_t[s];
} }

View File

@ -129,22 +129,22 @@ class Span {
} }
constexpr T &operator[](std::size_t i) noexcept { constexpr T &operator[](std::size_t i) noexcept {
ox::primitiveAssert(__FILE__, __LINE__, i < size(), "Span access overflow"); boundsCheck(__FILE__, __LINE__, i, size(), "Span access overflow");
return m_items[i]; return m_items[i];
} }
constexpr T const&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"); boundsCheck(__FILE__, __LINE__, i, size(), "Span access overflow");
return m_items[i]; return m_items[i];
} }
constexpr Span operator+(size_t i) const noexcept { constexpr Span operator+(size_t i) const noexcept {
ox::primitiveAssert(__FILE__, __LINE__, i < size(), "Span access overflow"); boundsCheck(__FILE__, __LINE__, i, size(), "Span access overflow");
return {m_items + i, m_size - i}; return {m_items + i, m_size - i};
} }
constexpr Span operator+=(size_t i) noexcept { constexpr Span operator+=(size_t i) noexcept {
ox::primitiveAssert(__FILE__, __LINE__, i < size(), "Span access overflow"); boundsCheck(__FILE__, __LINE__, i, size(), "Span access overflow");
m_items += i; m_items += i;
m_size -= i; m_size -= i;
return *this; return *this;

View File

@ -27,6 +27,48 @@ constexpr void swap(T &a, T &b) noexcept {
b = std::move(temp); b = std::move(temp);
} }
template<typename T, typename U>
constexpr bool cmp_equal(T const t, U const u) noexcept {
if constexpr(ox::is_signed_v<T> == ox::is_signed_v<U>) {
return t == u;
} else if constexpr(ox::is_signed_v<T>) {
return ox::Signed<T>{t} == u;
} else {
return t == ox::Signed<U>{u};
}
}
template<typename T, typename U>
constexpr bool cmp_less(T const t, U const u) noexcept {
if constexpr(ox::is_signed_v<T> == ox::is_signed_v<U>) {
return t < u;
} else if constexpr(ox::is_signed_v<T>) {
return ox::Signed<T>{t} < u;
} else {
return t < ox::Signed<U>{u};
}
}
template<typename T, typename U>
constexpr bool cmp_not_equal(T const t, U const u) noexcept {
return !std::cmp_equal(t, u);
}
template<typename T, typename U>
constexpr bool cmp_greater(T const t, U const u) noexcept {
return std::cmp_less(u, t);
}
template<typename T, typename U>
constexpr bool cmp_less_equal(T const t, U const u) noexcept {
return !std::cmp_less(u, t);
}
template<typename T, typename U>
constexpr bool cmp_greater_equal(T const t, U const u) noexcept {
return !std::cmp_less(t, u);
}
} }
#endif #endif

View File

@ -311,6 +311,8 @@ class Vector: detail::VectorAllocator<T, Allocator, SmallVectorSize> {
*/ */
constexpr Error unordered_erase(std::size_t pos) noexcept(useNoexcept); constexpr Error unordered_erase(std::size_t pos) noexcept(useNoexcept);
constexpr Error remove(T const &val);
constexpr void reserve(std::size_t cap) noexcept(useNoexcept); constexpr void reserve(std::size_t cap) noexcept(useNoexcept);
constexpr void shrink_to_fit() noexcept(useNoexcept); constexpr void shrink_to_fit() noexcept(useNoexcept);
@ -427,13 +429,13 @@ constexpr Vector<T, SmallVectorSize, Allocator> &Vector<T, SmallVectorSize, Allo
template<typename T, std::size_t SmallVectorSize, typename Allocator> template<typename T, std::size_t SmallVectorSize, typename Allocator>
constexpr T &Vector<T, SmallVectorSize, Allocator>::operator[](std::size_t i) noexcept { constexpr T &Vector<T, SmallVectorSize, Allocator>::operator[](std::size_t i) noexcept {
ox::primitiveAssert(__FILE__, __LINE__, i < size(), "Vector access overflow"); boundsCheck(__FILE__, __LINE__, i, size(), "Vector access overflow");
return m_items[i]; return m_items[i];
} }
template<typename T, std::size_t SmallVectorSize, typename Allocator> template<typename T, std::size_t SmallVectorSize, typename Allocator>
constexpr const T &Vector<T, SmallVectorSize, Allocator>::operator[](std::size_t i) const noexcept { constexpr const T &Vector<T, SmallVectorSize, Allocator>::operator[](std::size_t i) const noexcept {
ox::primitiveAssert(__FILE__, __LINE__, i < size(), "Vector access overflow"); boundsCheck(__FILE__, __LINE__, i, size(), "Vector access overflow");
return m_items[i]; return m_items[i];
} }
@ -659,6 +661,17 @@ constexpr Error Vector<T, SmallVectorSize, Allocator>::unordered_erase(std::size
return ox::Error(0); return ox::Error(0);
} }
template<typename T, std::size_t SmallVectorSize, typename Allocator>
constexpr ox::Error Vector<T, SmallVectorSize, Allocator>::remove(T const &val) {
for (size_t i{}; auto const &v : *this) {
if (v == val) {
return erase(i).error;
}
++i;
}
return ox::Error{1, "element not found"};
}
template<typename T, std::size_t SmallVectorSize, typename Allocator> template<typename T, std::size_t SmallVectorSize, typename Allocator>
constexpr void Vector<T, SmallVectorSize, Allocator>::reserve(std::size_t cap) noexcept(useNoexcept) { constexpr void Vector<T, SmallVectorSize, Allocator>::reserve(std::size_t cap) noexcept(useNoexcept) {
if (cap <= m_cap) { if (cap <= m_cap) {

View File

@ -5,9 +5,13 @@
* Add TileSheetV5. TileSheetV5 retains the bpp field for the sake of * Add TileSheetV5. TileSheetV5 retains the bpp field for the sake of
CompactTileSheet, but always store it pixel as 8 bpp for itself. CompactTileSheet, but always store it pixel as 8 bpp for itself.
* Add ability to move subsheets in the subsheet tree. * Add ability to move subsheets in the subsheet tree.
* Add Flip X and Flip Y button for TileSheet Editor. * Add Flip X and Flip Y functionality to TileSheet Editor.
* Add rotate functionality to TileSheet Editor.
* Add draw line tool to TileSheet editor
* Replace file picker combo boxes with a browse button and file picker, and * Replace file picker combo boxes with a browse button and file picker, and
support for dragging files from the project explorer. support for dragging files from the project explorer.
* Add ability to jump to a color in a Palette by double clicking on the
color from the TileSheet editor
* Add ability to create directories. * Add ability to create directories.
* Add ability to add files to specific directories. * Add ability to add files to specific directories.
* Add ability to delete files from the project explorer. * Add ability to delete files from the project explorer.
@ -15,3 +19,5 @@
* Fix Palette Editor to ignore keyboard input when popups are open. * Fix Palette Editor to ignore keyboard input when popups are open.
* Palette Editor move color mechanism now uses drag and drop. * Palette Editor move color mechanism now uses drag and drop.
* Add ability to reorder Palette pages. * Add ability to reorder Palette pages.
* Add warning for closing a tab with unsaved changes.
* Add ability to close a tab with Ctrl/Cmd-W

View File

@ -0,0 +1,23 @@
O1;net.drinkingtea.ox.TypeDescriptor;1;{
"fieldList" :
[
{
"fieldName" : "pages",
"subscriptLevels" : 2,
"subscriptStack" :
[
{
"subscriptType" : 4
},
{
"subscriptType" : 4
}
],
"typeId" : "B.uint16;0"
}
],
"preloadable" : true,
"primitiveType" : 5,
"typeName" : "net.drinkingtea.nostalgia.gfx.CompactPalette",
"typeVersion" : 1
}

View File

@ -0,0 +1,28 @@
O1;net.drinkingtea.ox.TypeDescriptor;1;{
"fieldList" :
[
{
"fieldName" : "bpp",
"typeId" : "B.int8;0"
},
{
"fieldName" : "defaultPalette",
"typeId" : "net.drinkingtea.ox.FileAddress;1"
},
{
"fieldName" : "pixels",
"subscriptLevels" : 1,
"subscriptStack" :
[
{
"subscriptType" : 4
}
],
"typeId" : "B.uint8;0"
}
],
"preloadable" : true,
"primitiveType" : 5,
"typeName" : "net.drinkingtea.nostalgia.gfx.CompactTileSheet",
"typeVersion" : 1
}

View File

@ -0,0 +1,23 @@
O1;net.drinkingtea.ox.TypeDescriptor;1;{
"fieldList" :
[
{
"fieldName" : "name",
"typeId" : "net.drinkingtea.ox.BasicString#8#;1"
},
{
"fieldName" : "colors",
"subscriptLevels" : 1,
"subscriptStack" :
[
{
"subscriptType" : 4
}
],
"typeId" : "net.drinkingtea.nostalgia.gfx.PaletteColor;2"
}
],
"primitiveType" : 5,
"typeName" : "net.drinkingtea.nostalgia.gfx.Palette.PalettePage",
"typeVersion" : 2
}

View File

@ -0,0 +1,31 @@
O1;net.drinkingtea.ox.TypeDescriptor;1;{
"fieldList" :
[
{
"fieldName" : "colorNames",
"subscriptLevels" : 1,
"subscriptStack" :
[
{
"subscriptType" : 4
}
],
"typeId" : "net.drinkingtea.ox.BasicString#8#;1"
},
{
"fieldName" : "pages",
"subscriptLevels" : 1,
"subscriptStack" :
[
{
"subscriptType" : 4
}
],
"typeId" : "net.drinkingtea.nostalgia.gfx.Palette.PalettePage;2"
}
],
"preloadable" : true,
"primitiveType" : 5,
"typeName" : "net.drinkingtea.nostalgia.gfx.Palette",
"typeVersion" : 5
}

View File

@ -0,0 +1,24 @@
O1;net.drinkingtea.ox.TypeDescriptor;1;{
"fieldList" :
[
{
"fieldName" : "r",
"typeId" : "B.uint8;0"
},
{
"fieldName" : "g",
"typeId" : "B.uint8;0"
},
{
"fieldName" : "b",
"typeId" : "B.uint8;0"
},
{
"fieldName" : "a",
"typeId" : "B.uint8;0"
}
],
"primitiveType" : 5,
"typeName" : "net.drinkingtea.nostalgia.gfx.PaletteColor",
"typeVersion" : 2
}

View File

@ -0,0 +1,46 @@
O1;net.drinkingtea.ox.TypeDescriptor;1;{
"fieldList" :
[
{
"fieldName" : "id",
"typeId" : "B.int32;0"
},
{
"fieldName" : "name",
"typeId" : "net.drinkingtea.ox.BasicString#8#;1"
},
{
"fieldName" : "rows",
"typeId" : "B.int32;0"
},
{
"fieldName" : "columns",
"typeId" : "B.int32;0"
},
{
"fieldName" : "subsheets",
"subscriptLevels" : 1,
"subscriptStack" :
[
{
"subscriptType" : 4
}
],
"typeId" : "net.drinkingtea.nostalgia.gfx.TileSheet.SubSheet;5"
},
{
"fieldName" : "pixels",
"subscriptLevels" : 1,
"subscriptStack" :
[
{
"subscriptType" : 4
}
],
"typeId" : "B.uint8;0"
}
],
"primitiveType" : 5,
"typeName" : "net.drinkingtea.nostalgia.gfx.TileSheet.SubSheet",
"typeVersion" : 5
}

View File

@ -0,0 +1,24 @@
O1;net.drinkingtea.ox.TypeDescriptor;1;{
"fieldList" :
[
{
"fieldName" : "bpp",
"typeId" : "B.int8;0"
},
{
"fieldName" : "idIt",
"typeId" : "B.int32;0"
},
{
"fieldName" : "defaultPalette",
"typeId" : "net.drinkingtea.ox.BasicString#8#;1"
},
{
"fieldName" : "subsheet",
"typeId" : "net.drinkingtea.nostalgia.gfx.TileSheet.SubSheet;5"
}
],
"primitiveType" : 5,
"typeName" : "net.drinkingtea.nostalgia.gfx.TileSheet",
"typeVersion" : 5
}

View File

@ -1,7 +1,7 @@
# module dir list # module dir list
add_subdirectory(gfx) add_subdirectory(gfx)
add_subdirectory(scene) add_subdirectory(sound)
# module libraries # module libraries
@ -13,8 +13,8 @@ add_library(
target_link_libraries( target_link_libraries(
NostalgiaKeelModules PUBLIC NostalgiaKeelModules PUBLIC
Keel Keel
NostalgiaCore-Keel NostalgiaGfx-Keel
NostalgiaScene-Keel NostalgiaSound-Keel
) )
install( install(
FILES FILES
@ -32,8 +32,8 @@ if(NOSTALGIA_BUILD_STUDIO)
target_link_libraries( target_link_libraries(
NostalgiaStudioModules PUBLIC NostalgiaStudioModules PUBLIC
StudioAppLib StudioAppLib
NostalgiaCore-Studio-ImGui NostalgiaGfx-Studio-ImGui
NostalgiaScene-Studio NostalgiaSound-Studio-ImGui
) )
install( install(
FILES FILES

View File

@ -16,23 +16,6 @@
namespace nostalgia::gfx { namespace nostalgia::gfx {
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";
@ -52,7 +35,7 @@ constexpr bool valid(TileSheetV1 const&ts) noexcept {
return (ts.bpp == 4 || ts.bpp == 8) && ts.pixels.size() == bytes; return (ts.bpp == 4 || ts.bpp == 8) && ts.pixels.size() == bytes;
} }
constexpr ox::Error repair(TileSheetV1 &ts, int bpp) noexcept { constexpr ox::Error repair(TileSheetV1 &ts, int const bpp) noexcept {
auto const bytes = static_cast<size_t>(ts.columns * ts.rows * PixelsPerTile) / (bpp == 4 ? 2 : 1); auto const bytes = static_cast<size_t>(ts.columns * ts.rows * PixelsPerTile) / (bpp == 4 ? 2 : 1);
ts.pixels.resize(bytes); ts.pixels.resize(bytes);
return {}; return {};
@ -101,7 +84,7 @@ constexpr bool valid(TileSheetV2 const&ts) noexcept {
return (ts.bpp == 4 || ts.bpp == 8) && valid(ts.subsheet, ts.bpp); return (ts.bpp == 4 || ts.bpp == 8) && valid(ts.subsheet, ts.bpp);
} }
constexpr void repair(TileSheetV2::SubSheet &ss, int bpp) noexcept { constexpr void repair(TileSheetV2::SubSheet &ss, int const 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);
ss.pixels.resize(bytes); ss.pixels.resize(bytes);
for (auto &s : ss.subsheets) { for (auto &s : ss.subsheets) {
@ -171,7 +154,7 @@ constexpr bool valid(TileSheetV3 const&ts) noexcept {
return (ts.bpp == 4 || ts.bpp == 8) && valid(ts.subsheet, ts.bpp); return (ts.bpp == 4 || ts.bpp == 8) && valid(ts.subsheet, ts.bpp);
} }
constexpr void repair(TileSheetV3::SubSheet &ss, int bpp) noexcept { constexpr void repair(TileSheetV3::SubSheet &ss, int const 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);
ss.pixels.resize(bytes); ss.pixels.resize(bytes);
for (auto &s : ss.subsheets) { for (auto &s : ss.subsheets) {
@ -265,7 +248,7 @@ constexpr bool valid(TileSheetV4 const&ts) noexcept {
return (ts.bpp == 4 || ts.bpp == 8) && valid(ts.subsheet, ts.bpp); return (ts.bpp == 4 || ts.bpp == 8) && valid(ts.subsheet, ts.bpp);
} }
constexpr void repair(TileSheetV4::SubSheet &ss, int bpp) noexcept { constexpr void repair(TileSheetV4::SubSheet &ss, int const bpp) noexcept {
if (ss.subsheets.empty()) { if (ss.subsheets.empty()) {
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);
ss.pixels.resize(bytes); ss.pixels.resize(bytes);
@ -367,7 +350,7 @@ constexpr bool valid(TileSheetV5 const&ts) noexcept {
return (ts.bpp == 4 || ts.bpp == 8) && valid(ts.subsheet); return (ts.bpp == 4 || ts.bpp == 8) && valid(ts.subsheet);
} }
constexpr void repair(TileSheetV5::SubSheet &ss, int const bpp) noexcept { constexpr void repair(TileSheetV5::SubSheet &ss) noexcept {
if (ss.subsheets.empty()) { if (ss.subsheets.empty()) {
auto const bytes = static_cast<size_t>(ss.columns * ss.rows * PixelsPerTile); auto const bytes = static_cast<size_t>(ss.columns * ss.rows * PixelsPerTile);
ss.pixels.resize(bytes); ss.pixels.resize(bytes);
@ -377,7 +360,7 @@ constexpr void repair(TileSheetV5::SubSheet &ss, int const bpp) noexcept {
ss.rows = -1; ss.rows = -1;
} }
for (auto &s : ss.subsheets) { for (auto &s : ss.subsheets) {
repair(s, bpp); repair(s);
} }
} }
@ -385,7 +368,7 @@ constexpr ox::Error repair(TileSheetV5 &ts) noexcept {
if (ts.bpp != 4 && ts.bpp != 8) { if (ts.bpp != 4 && ts.bpp != 8) {
return ox::Error{1, "Unable to repair TileSheet"}; return ox::Error{1, "Unable to repair TileSheet"};
} }
repair(ts.subsheet, ts.bpp); repair(ts.subsheet);
return {}; return {};
} }
@ -443,23 +426,24 @@ ox::Error resizeSubsheet(TileSheet::SubSheet &ss, ox::Size const&sz) noexcept;
[[nodiscard]] [[nodiscard]]
TileSheet::SubSheetIdx validateSubSheetIdx(TileSheet const&ts, TileSheet::SubSheetIdx idx) noexcept; TileSheet::SubSheetIdx validateSubSheetIdx(TileSheet const&ts, TileSheet::SubSheetIdx idx) noexcept;
[[nodiscard]]
TileSheet::SubSheet const&getSubSheet(
ox::SpanView<uint32_t> const&idx,
std::size_t idxIt,
TileSheet::SubSheet const&pSubsheet) noexcept;
[[nodiscard]] [[nodiscard]]
TileSheet::SubSheet &getSubSheet( TileSheet::SubSheet &getSubSheet(
ox::SpanView<uint32_t> const&idx, ox::SpanView<uint32_t> const&idx,
std::size_t idxIt, std::size_t idxIt,
TileSheet::SubSheet &pSubsheet) noexcept; TileSheet::SubSheet &pSubsheet) noexcept;
#if defined(__GNUC__) && __GNUC__ >= 13
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdangling-reference"
#endif
[[nodiscard]] [[nodiscard]]
TileSheet::SubSheet const&getSubSheet(TileSheet const&ts, ox::SpanView<uint32_t> const &idx) noexcept; TileSheet::SubSheet const&getSubSheet(TileSheet const&ts, ox::SpanView<uint32_t> const &idx) noexcept;
[[nodiscard]] [[nodiscard]]
TileSheet::SubSheet &getSubSheet(TileSheet &ts, ox::SpanView<uint32_t> const &idx) noexcept; TileSheet::SubSheet &getSubSheet(TileSheet &ts, ox::SpanView<uint32_t> const &idx) noexcept;
#if defined(__GNUC__) && __GNUC__ >= 13
#pragma GCC diagnostic pop
#endif
ox::Error addSubSheet(TileSheet &ts, TileSheet::SubSheetIdx const &idx) noexcept; ox::Error addSubSheet(TileSheet &ts, TileSheet::SubSheetIdx const &idx) noexcept;

View File

@ -1,5 +1,5 @@
add_library( add_library(
NostalgiaCore NostalgiaGfx
gfx.cpp gfx.cpp
tilesheet.cpp tilesheet.cpp
) )
@ -10,12 +10,12 @@ if(NOT BUILDCORE_TARGET STREQUAL "gba")
endif() endif()
target_include_directories( target_include_directories(
NostalgiaCore PUBLIC NostalgiaGfx PUBLIC
../include ../include
) )
target_link_libraries( target_link_libraries(
NostalgiaCore PUBLIC NostalgiaGfx PUBLIC
Turbine Turbine
) )
@ -26,7 +26,7 @@ endif()
install( install(
TARGETS TARGETS
NostalgiaCore NostalgiaGfx
DESTINATION DESTINATION
LIBRARY DESTINATION lib LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib ARCHIVE DESTINATION lib

View File

@ -1,15 +1,15 @@
add_library( add_library(
NostalgiaCore-GBA OBJECT NostalgiaGfx-GBA OBJECT
context.cpp context.cpp
gfx.cpp gfx.cpp
panic.cpp panic.cpp
) )
target_include_directories( target_include_directories(
NostalgiaCore-GBA PUBLIC NostalgiaGfx-GBA PUBLIC
../../include ../../include
) )
target_link_libraries( target_link_libraries(
NostalgiaCore-GBA PUBLIC NostalgiaGfx-GBA PUBLIC
TeaGBA TeaGBA
Keel Keel
Turbine Turbine
@ -17,5 +17,5 @@ target_link_libraries(
if(BUILDCORE_TARGET STREQUAL "gba") if(BUILDCORE_TARGET STREQUAL "gba")
set_source_files_properties(gfx.cpp PROPERTIES COMPILE_FLAGS -marm) set_source_files_properties(gfx.cpp PROPERTIES COMPILE_FLAGS -marm)
target_link_libraries(NostalgiaCore PUBLIC NostalgiaCore-GBA) target_link_libraries(NostalgiaGfx PUBLIC NostalgiaGfx-GBA)
endif() endif()

View File

@ -1,18 +1,18 @@
add_library( add_library(
NostalgiaCore-Keel NostalgiaGfx-Keel
keelmodule.cpp keelmodule.cpp
typeconv.cpp typeconv.cpp
) )
target_link_libraries( target_link_libraries(
NostalgiaCore-Keel PUBLIC NostalgiaGfx-Keel PUBLIC
Keel Keel
NostalgiaCore NostalgiaGfx
) )
install( install(
TARGETS TARGETS
NostalgiaCore-Keel NostalgiaGfx-Keel
DESTINATION DESTINATION
LIBRARY DESTINATION lib LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib ARCHIVE DESTINATION lib

View File

@ -1,9 +1,9 @@
target_sources( target_sources(
NostalgiaCore PRIVATE NostalgiaGfx PRIVATE
context.cpp context.cpp
gfx.cpp gfx.cpp
) )
target_link_libraries( target_link_libraries(
NostalgiaCore PUBLIC NostalgiaGfx PUBLIC
GlUtils GlUtils
) )

View File

@ -449,7 +449,7 @@ static void setSprite(
++i; ++i;
}; };
if (!s.flipX) { if (!s.flipX) {
for (auto yIt = 0; yIt < static_cast<int>(dim.y); ++yIt) { for (auto yIt = 0u; yIt < dim.y; ++yIt) {
for (auto xIt = 0u; xIt < dim.x; ++xIt) { for (auto xIt = 0u; xIt < dim.x; ++xIt) {
set(static_cast<int>(xIt), static_cast<int>(yIt), s.enabled); set(static_cast<int>(xIt), static_cast<int>(yIt), s.enabled);
} }

View File

@ -1,25 +1,25 @@
add_library(NostalgiaCore-Studio) add_library(NostalgiaGfx-Studio)
add_library( add_library(
NostalgiaCore-Studio-ImGui NostalgiaGfx-Studio-ImGui
studiomodule.cpp studiomodule.cpp
) )
target_link_libraries( target_link_libraries(
NostalgiaCore-Studio PUBLIC NostalgiaGfx-Studio PUBLIC
NostalgiaCore NostalgiaGfx
Studio Studio
) )
target_link_libraries( target_link_libraries(
NostalgiaCore-Studio-ImGui PUBLIC NostalgiaGfx-Studio-ImGui PUBLIC
NostalgiaCore-Studio NostalgiaGfx-Studio
) )
install( install(
TARGETS TARGETS
NostalgiaCore-Studio-ImGui NostalgiaGfx-Studio-ImGui
NostalgiaCore-Studio NostalgiaGfx-Studio
LIBRARY DESTINATION LIBRARY DESTINATION
${NOSTALGIA_DIST_MODULE} ${NOSTALGIA_DIST_MODULE}
) )

View File

@ -1,5 +1,5 @@
target_sources( target_sources(
NostalgiaCore-Studio-ImGui PRIVATE NostalgiaGfx-Studio-ImGui PRIVATE
paletteeditor-imgui.cpp paletteeditor-imgui.cpp
) )

View File

@ -1,6 +1,6 @@
target_sources( target_sources(
NostalgiaCore-Studio PRIVATE NostalgiaGfx-Studio PRIVATE
addcolorcommand.cpp addcolorcommand.cpp
addpagecommand.cpp addpagecommand.cpp
applycolorallpagescommand.cpp applycolorallpagescommand.cpp

View File

@ -94,6 +94,27 @@ ox::Error PaletteEditorImGui::saveItem() noexcept {
return m_sctx.project->writeObj(itemPath(), m_pal, ox::ClawFormat::Organic); return m_sctx.project->writeObj(itemPath(), m_pal, ox::ClawFormat::Organic);
} }
void PaletteEditorImGui::navigateTo(ox::StringViewCR arg) noexcept {
auto const args = ox::split<2>(arg, ';');
if (args.size() < 2) {
return;
}
auto const &color = args[0];
auto const &page = args[1];
{
auto const [c, err] = atoi(color);
if (!err && static_cast<size_t>(c) < colorCnt(m_pal)) {
m_selectedColorRow = static_cast<size_t>(c);
}
}
{
auto const [pg, err] = atoi(page);
if (!err && static_cast<size_t>(pg) < m_pal.pages.size()) {
m_page = static_cast<size_t>(pg);
}
}
}
void PaletteEditorImGui::drawColumnLeftAlign(ox::CStringView txt) noexcept { void PaletteEditorImGui::drawColumnLeftAlign(ox::CStringView txt) noexcept {
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("%s", txt.c_str()); ImGui::Text("%s", txt.c_str());

View File

@ -45,6 +45,8 @@ class PaletteEditorImGui: public studio::Editor {
protected: protected:
ox::Error saveItem() noexcept final; ox::Error saveItem() noexcept final;
void navigateTo(ox::StringViewCR arg) noexcept override;
private: private:
static void drawColumnLeftAlign(ox::CStringView txt) noexcept; static void drawColumnLeftAlign(ox::CStringView txt) noexcept;

View File

@ -1,5 +1,5 @@
target_sources( target_sources(
NostalgiaCore-Studio PRIVATE NostalgiaGfx-Studio PRIVATE
tilesheeteditorview.cpp tilesheeteditorview.cpp
tilesheeteditormodel.cpp tilesheeteditormodel.cpp
tilesheetpixelgrid.cpp tilesheetpixelgrid.cpp
@ -7,12 +7,12 @@ target_sources(
) )
target_sources( target_sources(
NostalgiaCore-Studio-ImGui PRIVATE NostalgiaGfx-Studio-ImGui PRIVATE
tilesheeteditor-imgui.cpp tilesheeteditor-imgui.cpp
) )
target_link_libraries( target_link_libraries(
NostalgiaCore-Studio-ImGui PUBLIC NostalgiaGfx-Studio-ImGui PUBLIC
lodepng lodepng
) )

View File

@ -1,5 +1,5 @@
target_sources( target_sources(
NostalgiaCore-Studio PRIVATE NostalgiaGfx-Studio PRIVATE
addsubsheetcommand.cpp addsubsheetcommand.cpp
cutpastecommand.cpp cutpastecommand.cpp
deletetilescommand.cpp deletetilescommand.cpp

View File

@ -63,7 +63,7 @@ DrawCommand::DrawCommand(
TileSheet &img, TileSheet &img,
TileSheet::SubSheetIdx subSheetIdx, TileSheet::SubSheetIdx subSheetIdx,
std::size_t idx, std::size_t idx,
int palIdx) noexcept: int const palIdx) noexcept:
m_img(img), m_img(img),
m_subSheetIdx(std::move(subSheetIdx)), m_subSheetIdx(std::move(subSheetIdx)),
m_palIdx(palIdx) { m_palIdx(palIdx) {
@ -75,7 +75,7 @@ DrawCommand::DrawCommand(
TileSheet &img, TileSheet &img,
TileSheet::SubSheetIdx subSheetIdx, TileSheet::SubSheetIdx subSheetIdx,
ox::SpanView<std::size_t> const&idxList, ox::SpanView<std::size_t> const&idxList,
int palIdx) noexcept: int const palIdx) noexcept:
m_img(img), m_img(img),
m_subSheetIdx(std::move(subSheetIdx)), m_subSheetIdx(std::move(subSheetIdx)),
m_palIdx(palIdx) { m_palIdx(palIdx) {
@ -123,9 +123,11 @@ void DrawCommand::lineUpdate(ox::Point a, ox::Point b) noexcept {
for (int32_t i{}; i < range; ++i) { for (int32_t i{}; i < range; ++i) {
auto const idx = ptToIdx(x, y + i * mod, ss.columns * TileWidth); auto const idx = ptToIdx(x, y + i * mod, ss.columns * TileWidth);
if (idx < ss.pixels.size()) { if (idx < ss.pixels.size()) {
if (m_palIdx != getPixel(ss, idx)) {
m_changes.emplace_back(static_cast<uint32_t>(idx), getPixel(ss, idx)); m_changes.emplace_back(static_cast<uint32_t>(idx), getPixel(ss, idx));
} }
} }
}
}); });
std::ignore = redo(); std::ignore = redo();
} }
@ -154,4 +156,8 @@ TileSheet::SubSheetIdx const&DrawCommand::subsheetIdx() const noexcept {
return m_subSheetIdx; return m_subSheetIdx;
} }
void DrawCommand::finish() noexcept {
setObsolete(m_changes.empty());
}
} }

View File

@ -52,6 +52,8 @@ class DrawCommand: public TileSheetCommand {
[[nodiscard]] [[nodiscard]]
TileSheet::SubSheetIdx const&subsheetIdx() const noexcept override; TileSheet::SubSheetIdx const&subsheetIdx() const noexcept override;
void finish() noexcept;
}; };
} }

View File

@ -36,22 +36,13 @@ OX_MODEL_BEGIN(TileSheetEditorConfig)
OX_MODEL_END() OX_MODEL_END()
static ox::Vector<uint32_t> normalizePixelSizes( static ox::Vector<uint32_t> normalizePixelSizes(
ox::Vector<uint8_t> const&inPixels, ox::Vector<uint8_t> const&inPixels) noexcept {
int const bpp) noexcept {
uint_t const bytesPerTile = bpp == 8 ? PixelsPerTile : PixelsPerTile / 2;
ox::Vector<uint32_t> outPixels; ox::Vector<uint32_t> outPixels;
if (bytesPerTile == 64) { // 8 BPP outPixels.reserve(inPixels.size());
outPixels.resize(inPixels.size()); outPixels.resize(inPixels.size());
for (std::size_t i = 0; i < inPixels.size(); ++i) { for (std::size_t i = 0; i < inPixels.size(); ++i) {
outPixels[i] = inPixels[i]; outPixels[i] = inPixels[i];
} }
} else { // 4 BPP
outPixels.resize(inPixels.size() * 2);
for (std::size_t i = 0; i < inPixels.size(); ++i) {
outPixels[i * 2 + 0] = inPixels[i] & 0xF;
outPixels[i * 2 + 1] = inPixels[i] >> 4;
}
}
return outPixels; return outPixels;
} }
@ -248,29 +239,22 @@ void TileSheetEditorImGui::draw(studio::StudioContext&) noexcept {
ImGui::EndChild(); ImGui::EndChild();
ImGui::BeginChild("OperationsBox", {0, 35}, ImGuiWindowFlags_NoTitleBar); ImGui::BeginChild("OperationsBox", {0, 35}, ImGuiWindowFlags_NoTitleBar);
{ {
size_t i{}; if (ImGui::BeginCombo("##Operations", "Operations", 0)) {
if (ig::ComboBox("##Operations", ox::SpanView<ox::CStringView>{{ if (ImGui::Selectable("Flip X", false)) {
ox::CStringView{"Operations"},
ox::CStringView{"Flip X"},
ox::CStringView{"Flip Y"},
ox::CStringView{"Rotate Left"},
ox::CStringView{"Rotate Right"},
}}, i)) {
switch (i) {
case 1:
oxLogError(m_model.flipX()); oxLogError(m_model.flipX());
break;
case 2:
oxLogError(m_model.flipY());
break;
case 3:
oxLogError(m_model.rotateLeft());
break;
case 4:
oxLogError(m_model.rotateRight());
break;
default:;
} }
if (ImGui::Selectable("Flip Y", false)) {
oxLogError(m_model.flipY());
}
ImGui::BeginDisabled(!m_model.rotateEligible());
if (ImGui::Selectable("Rotate Left", false)) {
oxLogError(m_model.rotateLeft());
}
if (ImGui::Selectable("Rotate Right", false)) {
oxLogError(m_model.rotateRight());
}
ImGui::EndDisabled();
ImGui::EndCombo();
} }
} }
ImGui::EndChild(); ImGui::EndChild();
@ -416,12 +400,11 @@ void TileSheetEditorImGui::showSubsheetEditor() noexcept {
ox::Error TileSheetEditorImGui::exportSubhseetToPng(int const scale) const noexcept { ox::Error TileSheetEditorImGui::exportSubhseetToPng(int const scale) const noexcept {
OX_REQUIRE(path, studio::saveFile({{"PNG", "png"}})); OX_REQUIRE(path, studio::saveFile({{"PNG", "png"}}));
// subsheet to png // subsheet to png
auto const&img = m_model.img();
auto const&s = m_model.activeSubSheet(); auto const&s = m_model.activeSubSheet();
auto const&pal = m_model.pal(); auto const&pal = m_model.pal();
auto const width = s.columns * TileWidth; auto const width = s.columns * TileWidth;
auto const height = s.rows * TileHeight; auto const height = s.rows * TileHeight;
auto pixels = normalizePixelSizes(s.pixels, img.bpp); auto pixels = normalizePixelSizes(s.pixels);
pixels = normalizePixelArrangement(pixels, s.columns, scale); pixels = normalizePixelArrangement(pixels, s.columns, scale);
auto const err = toPngFile( auto const err = toPngFile(
path, path,
@ -572,9 +555,15 @@ void TileSheetEditorImGui::drawPaletteMenu() noexcept {
label.c_str(), rowSelected, ImGuiSelectableFlags_SpanAllColumns)) { label.c_str(), rowSelected, ImGuiSelectableFlags_SpanAllColumns)) {
m_view.setPalIdx(i); m_view.setPalIdx(i);
} }
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0)) {
studio::navigateTo(
m_sctx,
m_model.palPath(),
ox::sfmt("{};{}", i, m_model.palettePage()));
}
// Column: color RGB // Column: color RGB
ImGui::TableNextColumn(); ImGui::TableNextColumn();
auto ic = ImGui::GetColorU32(ImVec4(redf(c), greenf(c), bluef(c), 1)); auto const ic = ImGui::GetColorU32({redf(c), greenf(c), bluef(c), 1});
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ic); ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ic);
ImGui::TableNextColumn(); 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() : "";
@ -653,7 +642,7 @@ void TileSheetEditorImGui::ExportMenu::draw(turbine::Context &tctx) noexcept {
constexpr auto popupSz = ImVec2{popupWidth, popupHeight}; constexpr auto popupSz = ImVec2{popupWidth, popupHeight};
if (ig::BeginPopup(tctx, popupName, m_show, popupSz)) { if (ig::BeginPopup(tctx, popupName, m_show, popupSz)) {
ImGui::InputInt("Scale", &m_scale); ImGui::InputInt("Scale", &m_scale);
m_scale = ox::clamp(m_scale, 1, 50); m_scale = ox::clamp(m_scale, 1, 135);
if (ig::PopupControlsOkCancel(popupWidth, m_show) == ig::PopupResponse::OK) { if (ig::PopupControlsOkCancel(popupWidth, m_show) == ig::PopupResponse::OK) {
inputSubmitted.emit(m_scale); inputSubmitted.emit(m_scale);
} }

View File

@ -18,14 +18,13 @@
#include "commands/drawcommand.hpp" #include "commands/drawcommand.hpp"
#include "commands/flipcommand.hpp" #include "commands/flipcommand.hpp"
#include "commands/inserttilescommand.hpp" #include "commands/inserttilescommand.hpp"
#include "commands/movesubsheetcommand.hpp"
#include "commands/palettechangecommand.hpp" #include "commands/palettechangecommand.hpp"
#include "commands/rmsubsheetcommand.hpp" #include "commands/rmsubsheetcommand.hpp"
#include "commands/rotatecommand.hpp"
#include "commands/updatesubsheetcommand.hpp" #include "commands/updatesubsheetcommand.hpp"
#include "tilesheeteditormodel.hpp" #include "tilesheeteditormodel.hpp"
#include "commands/movesubsheetcommand.hpp"
#include "commands/rotatecommand.hpp"
namespace nostalgia::gfx { namespace nostalgia::gfx {
// delete pixels of all non-leaf nodes // delete pixels of all non-leaf nodes
@ -172,7 +171,7 @@ void TileSheetEditorModel::drawLineCommand(ox::Point const &pt, std::size_t cons
if (m_ongoingDrawCommand) { if (m_ongoingDrawCommand) {
m_ongoingDrawCommand->lineUpdate(m_lineStartPt, pt); m_ongoingDrawCommand->lineUpdate(m_lineStartPt, pt);
m_updated = true; m_updated = true;
} else if (getPixel(activeSubSheet, idx) != palIdx) { } else {
std::ignore = pushCommand(ox::make<DrawCommand>( std::ignore = pushCommand(ox::make<DrawCommand>(
m_img, m_activeSubsSheetIdx, idx, static_cast<int>(palIdx))); m_img, m_activeSubsSheetIdx, idx, static_cast<int>(palIdx)));
m_lineStartPt = pt; m_lineStartPt = pt;
@ -180,8 +179,11 @@ void TileSheetEditorModel::drawLineCommand(ox::Point const &pt, std::size_t cons
} }
void TileSheetEditorModel::endDrawCommand() noexcept { void TileSheetEditorModel::endDrawCommand() noexcept {
if (m_ongoingDrawCommand) {
m_ongoingDrawCommand->finish();
m_ongoingDrawCommand = nullptr; m_ongoingDrawCommand = nullptr;
} }
}
void TileSheetEditorModel::addSubsheet(TileSheet::SubSheetIdx const&parentIdx) noexcept { void TileSheetEditorModel::addSubsheet(TileSheet::SubSheetIdx const&parentIdx) noexcept {
std::ignore = pushCommand(ox::make<AddSubSheetCommand>(m_img, parentIdx)); std::ignore = pushCommand(ox::make<AddSubSheetCommand>(m_img, parentIdx));
@ -352,6 +354,16 @@ ox::Error TileSheetEditorModel::flipY() noexcept {
return pushCommand(ox::make<FlipYCommand>(m_img, m_activeSubsSheetIdx, a, b)); return pushCommand(ox::make<FlipYCommand>(m_img, m_activeSubsSheetIdx, a, b));
} }
bool TileSheetEditorModel::rotateEligible() const noexcept {
if (m_selection) {
auto const w = m_selection->b.x - m_selection->a.x;
auto const h = m_selection->b.y - m_selection->a.y;
return w == h;
}
auto const &ss = activeSubSheet();
return ss.rows == ss.columns;
}
ox::Error TileSheetEditorModel::moveSubSheet(TileSheet::SubSheetIdx src, TileSheet::SubSheetIdx dst) 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<MoveSubSheetCommand>(m_img, std::move(src), std::move(dst)));
} }

View File

@ -138,6 +138,9 @@ class TileSheetEditorModel: public ox::SignalHandler {
ox::Error flipY() noexcept; ox::Error flipY() noexcept;
[[nodiscard]]
bool rotateEligible() const noexcept;
ox::Error moveSubSheet(TileSheet::SubSheetIdx src, TileSheet::SubSheetIdx dst) noexcept; ox::Error moveSubSheet(TileSheet::SubSheetIdx src, TileSheet::SubSheetIdx dst) noexcept;
private: private:

View File

@ -22,7 +22,12 @@ ox::Error TileSheetGrid::buildShader() noexcept {
} }
void TileSheetGrid::draw(bool update, ox::Vec2 const&scroll) noexcept { void TileSheetGrid::draw(bool update, ox::Vec2 const&scroll) noexcept {
// the lines just show up bigger on Windows for some reason
if constexpr(ox::defines::OS == ox::OS::Windows) {
glLineWidth(3 * m_pixelSizeMod * 0.25f);
} else {
glLineWidth(3 * m_pixelSizeMod * 0.5f); glLineWidth(3 * m_pixelSizeMod * 0.5f);
}
glUseProgram(m_shader); glUseProgram(m_shader);
glBindVertexArray(m_bufferSet.vao); glBindVertexArray(m_bufferSet.vao);
if (update) { if (update) {

View File

@ -187,7 +187,11 @@ TileSheet::SubSheetIdx validateSubSheetIdx(TileSheet const&ts, TileSheet::SubShe
return validateSubSheetIdx(std::move(idx), 0, ts.subsheet); return validateSubSheetIdx(std::move(idx), 0, ts.subsheet);
} }
TileSheet::SubSheet const&getSubSheet( #if defined(__GNUC__) && __GNUC__ >= 13
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdangling-reference"
#endif
static TileSheet::SubSheet const&getSubSheet(
ox::SpanView<uint32_t> const &idx, ox::SpanView<uint32_t> const &idx,
std::size_t const idxIt, std::size_t const idxIt,
TileSheet::SubSheet const &pSubsheet) noexcept { TileSheet::SubSheet const &pSubsheet) noexcept {
@ -200,6 +204,9 @@ TileSheet::SubSheet const&getSubSheet(
} }
return getSubSheet(idx, idxIt + 1, pSubsheet.subsheets[currentIdx]); return getSubSheet(idx, idxIt + 1, pSubsheet.subsheets[currentIdx]);
} }
#if defined(__GNUC__) && __GNUC__ >= 13
#pragma GCC diagnostic pop
#endif
TileSheet::SubSheet &getSubSheet( TileSheet::SubSheet &getSubSheet(
ox::SpanView<uint32_t> const &idx, ox::SpanView<uint32_t> const &idx,
@ -211,6 +218,10 @@ TileSheet::SubSheet &getSubSheet(
return getSubSheet(idx, idxIt + 1, pSubsheet.subsheets[idx[idxIt]]); return getSubSheet(idx, idxIt + 1, pSubsheet.subsheets[idx[idxIt]]);
} }
#if defined(__GNUC__) && __GNUC__ >= 13
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdangling-reference"
#endif
TileSheet::SubSheet const&getSubSheet(TileSheet const &ts, ox::SpanView<uint32_t> const &idx) noexcept { TileSheet::SubSheet const&getSubSheet(TileSheet const &ts, ox::SpanView<uint32_t> const &idx) noexcept {
return gfx::getSubSheet(idx, 0, ts.subsheet); return gfx::getSubSheet(idx, 0, ts.subsheet);
} }
@ -218,6 +229,9 @@ TileSheet::SubSheet const&getSubSheet(TileSheet const &ts, ox::SpanView<uint32_t
TileSheet::SubSheet &getSubSheet(TileSheet &ts, ox::SpanView<uint32_t> const &idx) noexcept { TileSheet::SubSheet &getSubSheet(TileSheet &ts, ox::SpanView<uint32_t> const &idx) noexcept {
return gfx::getSubSheet(idx, 0, ts.subsheet); return gfx::getSubSheet(idx, 0, ts.subsheet);
} }
#if defined(__GNUC__) && __GNUC__ >= 13
#pragma GCC diagnostic pop
#endif
ox::Error addSubSheet(TileSheet &ts, TileSheet::SubSheetIdx const&idx) noexcept { ox::Error addSubSheet(TileSheet &ts, TileSheet::SubSheetIdx const&idx) noexcept {
auto &parent = getSubSheet(ts, idx); auto &parent = getSubSheet(ts, idx);

View File

@ -1,11 +1,11 @@
add_executable( add_executable(
NostalgiaCoreTest NostalgiaGfxTest
tests.cpp tests.cpp
) )
target_link_libraries( target_link_libraries(
NostalgiaCoreTest NostalgiaGfxTest
NostalgiaCore NostalgiaGfx
) )
add_test("[NostalgiaCore] readWriteTileSheet" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/NostalgiaCoreTest readWriteTileSheet) add_test("[NostalgiaGfx] readWriteTileSheet" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/NostalgiaGfxTest readWriteTileSheet)

View File

@ -3,7 +3,7 @@
*/ */
#include <nostalgia/gfx/keelmodule.hpp> #include <nostalgia/gfx/keelmodule.hpp>
#include <nostalgia/scene/keelmodule.hpp> #include <nostalgia/sound/keelmodule.hpp>
namespace nostalgia { namespace nostalgia {
@ -14,7 +14,7 @@ void registerKeelModules() noexcept {
} }
modulesRegistered = true; modulesRegistered = true;
keel::registerModule(gfx::keelModule()); keel::registerModule(gfx::keelModule());
keel::registerModule(scene::keelModule()); keel::registerModule(sound::keelModule());
} }
} }

View File

@ -1,27 +0,0 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#pragma once
#include <nostalgia/gfx/context.hpp>
#include "scenestatic.hpp"
namespace nostalgia::scene {
class Scene {
private:
SceneStatic const&m_sceneStatic;
public:
explicit Scene(SceneStatic const&sceneStatic) noexcept;
ox::Error setupDisplay(gfx::Context &ctx) const noexcept;
private:
void setupLayer(gfx::Context&, ox::Vector<uint16_t> const&layer, unsigned layerNo) const noexcept;
};
}

View File

@ -1,228 +0,0 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#pragma once
#include <ox/fs/fs.hpp>
#include <ox/std/error.hpp>
#include <ox/std/size.hpp>
#include <ox/std/types.hpp>
#include <ox/std/vector.hpp>
#include <nostalgia/gfx/tilesheet.hpp>
namespace nostalgia::scene {
struct SpriteDoc {
constexpr static auto TypeName = "net.drinkingtea.nostalgia.scene.SpriteDoc";
constexpr static auto TypeVersion = 1;
constexpr static auto Preloadable = true;
ox::String tilesheetPath;
ox::Vector<gfx::SubSheetId> subsheetId;
};
struct TileDoc {
constexpr static auto TypeName = "net.drinkingtea.nostalgia.scene.TileDoc";
constexpr static auto TypeVersion = 1;
constexpr static auto Preloadable = true;
gfx::SubSheetId subsheetId = -1;
ox::String subsheetPath;
uint8_t type = 0;
ox::Array<uint8_t, 4> layerAttachments;
[[nodiscard]]
constexpr ox::Result<gfx::SubSheetId> getSubsheetId(gfx::TileSheet const&ts) const noexcept {
// prefer the already present ID
if (subsheetId > -1) {
return subsheetId;
}
return getIdFor(ts, subsheetPath);
}
[[nodiscard]]
constexpr ox::Result<ox::StringView> getSubsheetPath(
gfx::TileSheet const&ts) const noexcept {
// prefer the already present path
if (!subsheetPath.len()) {
return gfx::getNameFor(ts, subsheetId);
}
return ox::StringView(subsheetPath);
}
};
OX_MODEL_BEGIN(TileDoc)
OX_MODEL_FIELD_RENAME(subsheetId, subsheet_id)
OX_MODEL_FIELD_RENAME(subsheetPath, subsheet_path)
OX_MODEL_FIELD(type)
OX_MODEL_FIELD_RENAME(layerAttachments, layer_attachments)
OX_MODEL_END()
struct SceneDoc {
using TileMapRow = ox::Vector<TileDoc>;
using TileMapLayer = ox::Vector<TileMapRow>;
using TileMap = ox::Vector<TileMapLayer>;
constexpr static auto TypeName = "net.drinkingtea.nostalgia.scene.SceneDoc";
constexpr static auto TypeVersion = 1;
constexpr static auto Preloadable = true;
ox::String tilesheet; // path
ox::Vector<ox::String> palettes; // paths
TileMap tiles;
[[nodiscard]]
constexpr ox::Size size(std::size_t layerIdx) const noexcept {
const auto &layer = this->tiles[layerIdx];
const auto rowCnt = static_cast<int>(layer.size());
if (!rowCnt) {
return {};
}
auto colCnt = layer[0].size();
// find shortest row (they should all be the same, but you know this data
// could come from a file)
for (auto const&row : layer) {
colCnt = ox::min(colCnt, row.size());
}
return {static_cast<int>(colCnt), rowCnt};
}
};
OX_MODEL_BEGIN(SceneDoc)
OX_MODEL_FIELD(tilesheet)
OX_MODEL_FIELD(palettes)
OX_MODEL_FIELD(tiles)
OX_MODEL_END()
constexpr void setTopEdge(uint8_t &layerAttachments, unsigned val) noexcept {
const auto val8 = static_cast<uint8_t>(val);
layerAttachments = (layerAttachments & 0b11111100) | val8;
}
constexpr void setBottomEdge(uint8_t &layerAttachments, unsigned val) noexcept {
const auto val8 = static_cast<uint8_t>(val);
layerAttachments = (layerAttachments & 0b11110011) | static_cast<uint8_t>(val8 << 2);
}
constexpr void setLeftEdge(uint8_t &layerAttachments, unsigned val) noexcept {
const auto val8 = static_cast<uint8_t>(val);
layerAttachments = (layerAttachments & 0b11001111) | static_cast<uint8_t>(val8 << 4);
}
constexpr void setRightEdge(uint8_t &layerAttachments, unsigned val) noexcept {
const auto val8 = static_cast<uint8_t>(val);
layerAttachments = (layerAttachments & 0b00111111) | static_cast<uint8_t>(val8 << 6);
}
[[nodiscard]]
constexpr unsigned topEdge(uint8_t layerAttachments) noexcept {
return layerAttachments & 0b11;
}
[[nodiscard]]
constexpr unsigned bottomEdge(uint8_t layerAttachments) noexcept {
return (layerAttachments >> 2) & 0b11;
}
[[nodiscard]]
constexpr unsigned leftEdge(uint8_t layerAttachments) noexcept {
return (layerAttachments >> 4) & 0b11;
}
[[nodiscard]]
constexpr unsigned rightEdge(uint8_t layerAttachments) noexcept {
return (layerAttachments >> 6) & 0b11;
}
struct SceneStatic {
constexpr static auto TypeName = "net.drinkingtea.nostalgia.scene.SceneStatic";
constexpr static auto TypeVersion = 1;
constexpr static auto Preloadable = true;
struct Tile {
uint16_t &tileMapIdx;
uint8_t &tileType;
uint8_t &layerAttachments;
constexpr Tile(uint16_t &pTileMapIdx, uint8_t &pTileType, uint8_t &pLayerAttachments) noexcept:
tileMapIdx(pTileMapIdx),
tileType(pTileType),
layerAttachments(pLayerAttachments) {
}
};
struct Layer {
uint16_t &columns;
uint16_t &rows;
ox::Vector<uint16_t> &tileMapIdx;
ox::Vector<uint8_t> &tileType;
ox::Vector<uint8_t> &layerAttachments;
constexpr Layer(
uint16_t &pColumns,
uint16_t &pRows,
ox::Vector<uint16_t> &pTileMapIdx,
ox::Vector<uint8_t> &pTileType,
ox::Vector<uint8_t> &pLayerAttachments) noexcept:
columns(pColumns),
rows(pRows),
tileMapIdx(pTileMapIdx),
tileType(pTileType),
layerAttachments(pLayerAttachments) {
}
[[nodiscard]]
constexpr Tile tile(std::size_t i) noexcept {
return {tileMapIdx[i], tileType[i], layerAttachments[i]};
}
constexpr auto setDimensions(ox::Size dim) noexcept {
columns = static_cast<uint16_t>(dim.width);
rows = static_cast<uint16_t>(dim.height);
const auto tileCnt = static_cast<unsigned>(columns * rows);
tileMapIdx.resize(tileCnt);
tileType.resize(tileCnt);
layerAttachments.resize(tileCnt);
}
};
ox::FileAddress tilesheet;
ox::Vector<ox::FileAddress> palettes;
// tile layer data
ox::Vector<uint16_t> columns;
ox::Vector<uint16_t> rows;
ox::Vector<ox::Vector<uint16_t>> tileMapIdx;
ox::Vector<ox::Vector<uint8_t>> tileType;
ox::Vector<ox::Vector<uint8_t>> layerAttachments;
[[nodiscard]]
constexpr Layer layer(std::size_t i) noexcept {
return {
columns[i],
rows[i],
tileMapIdx[i],
tileType[i],
layerAttachments[i],
};
}
constexpr auto setLayerCnt(std::size_t layerCnt) noexcept {
this->layerAttachments.resize(layerCnt);
this->columns.resize(layerCnt);
this->rows.resize(layerCnt);
this->tileMapIdx.resize(layerCnt);
this->tileType.resize(layerCnt);
}
};
OX_MODEL_BEGIN(SceneStatic)
OX_MODEL_FIELD(tilesheet)
OX_MODEL_FIELD(palettes)
OX_MODEL_FIELD(columns)
OX_MODEL_FIELD(rows)
OX_MODEL_FIELD(tileMapIdx)
OX_MODEL_FIELD(tileType)
OX_MODEL_FIELD(layerAttachments)
OX_MODEL_END()
}

View File

@ -1,17 +0,0 @@
add_library(
NostalgiaScene-Keel
keelmodule.cpp
typeconv.cpp
)
target_link_libraries(
NostalgiaScene-Keel PUBLIC
Keel
NostalgiaScene
)
install(
TARGETS
NostalgiaScene-Keel
LIBRARY DESTINATION
${NOSTALGIA_DIST_MODULE}
)

View File

@ -1,51 +0,0 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include <keel/module.hpp>
#include <nostalgia/scene/scenestatic.hpp>
#include "typeconv.hpp"
namespace nostalgia::scene {
static class: public keel::Module {
private:
SceneDocToSceneStaticConverter m_sceneDocToSceneStaticConverter;
public:
[[nodiscard]]
ox::String id() const noexcept override {
return ox::String("net.drinkingtea.nostalgia.scene");
}
[[nodiscard]]
ox::Vector<keel::TypeDescGenerator> types() const noexcept override {
return {
keel::generateTypeDesc<SceneDoc>,
keel::generateTypeDesc<SceneStatic>,
};
}
[[nodiscard]]
ox::Vector<const keel::BaseConverter*> converters() const noexcept override {
return {
&m_sceneDocToSceneStaticConverter,
};
}
[[nodiscard]]
ox::Vector<keel::PackTransform> packTransforms() const noexcept override {
return {
keel::transformRule<SceneDoc, SceneStatic>,
};
}
} mod;
const keel::Module *keelModule() noexcept {
return &mod;
}
}

View File

@ -1,68 +0,0 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include <nostalgia/gfx/gfx.hpp>
#include <keel/media.hpp>
#include "typeconv.hpp"
namespace nostalgia::scene {
[[nodiscard]]
constexpr unsigned adjustLayerAttachment(unsigned layer, unsigned attachment) noexcept {
if (attachment == 0) {
return layer;
} else {
return attachment - 1;
}
}
constexpr void setLayerAttachments(unsigned layer, TileDoc const&srcTile, SceneStatic::Tile &dstTile) noexcept {
setTopEdge(
dstTile.layerAttachments,
adjustLayerAttachment(layer, srcTile.layerAttachments[0]));
setBottomEdge(
dstTile.layerAttachments,
adjustLayerAttachment(layer, srcTile.layerAttachments[1]));
setLeftEdge(
dstTile.layerAttachments,
adjustLayerAttachment(layer, srcTile.layerAttachments[2]));
setRightEdge(
dstTile.layerAttachments,
adjustLayerAttachment(layer, srcTile.layerAttachments[3]));
}
ox::Error SceneDocToSceneStaticConverter::convert(
keel::Context &ctx,
SceneDoc &src,
SceneStatic &dst) const noexcept {
OX_REQUIRE(ts, keel::readObj<gfx::TileSheet>(ctx, src.tilesheet));
const auto layerCnt = src.tiles.size();
dst.setLayerCnt(layerCnt);
dst.tilesheet = ox::FileAddress(src.tilesheet);
dst.palettes.reserve(src.palettes.size());
for (const auto &pal : src.palettes) {
dst.palettes.emplace_back(pal);
}
for (auto layerIdx = 0u; const auto &layer : src.tiles) {
const auto layerDim = src.size(layerIdx);
auto dstLayer = dst.layer(layerIdx);
dstLayer.setDimensions(layerDim);
for (auto tileIdx = 0u; const auto &row : layer) {
for (const auto &srcTile : row) {
auto dstTile = dstLayer.tile(tileIdx);
dstTile.tileType = srcTile.type;
OX_REQUIRE(path, srcTile.getSubsheetPath(*ts));
OX_REQUIRE(mapIdx, getTileOffset(*ts, path));
dstTile.tileMapIdx = static_cast<uint16_t>(mapIdx);
setLayerAttachments(layerIdx, srcTile, dstTile);
++tileIdx;
}
}
++layerIdx;
}
return {};
}
}

View File

@ -1,17 +0,0 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#pragma once
#include <keel/typeconv.hpp>
#include <nostalgia/scene/scenestatic.hpp>
namespace nostalgia::scene {
class SceneDocToSceneStaticConverter: public keel::Converter<SceneDoc, SceneStatic> {
ox::Error convert(keel::Context&, SceneDoc &src, SceneStatic &dst) const noexcept final;
};
}

View File

@ -1,54 +0,0 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include <nostalgia/gfx/gfx.hpp>
#include <nostalgia/scene/scene.hpp>
namespace nostalgia::scene {
Scene::Scene(SceneStatic const&sceneStatic) noexcept:
m_sceneStatic(sceneStatic) {
}
ox::Error Scene::setupDisplay(gfx::Context &ctx) const noexcept {
if (m_sceneStatic.palettes.empty()) {
return ox::Error(1, "Scene has no palettes");
}
auto const&palette = m_sceneStatic.palettes[0];
OX_RETURN_ERROR(gfx::loadBgTileSheet(ctx, 0, m_sceneStatic.tilesheet));
OX_RETURN_ERROR(gfx::loadBgPalette(ctx, 0, palette));
// disable all backgrounds
gfx::setBgStatus(ctx, 0);
for (auto layerNo = 0u; auto const&layer : m_sceneStatic.tileMapIdx) {
setupLayer(ctx, layer, layerNo);
++layerNo;
}
return {};
}
void Scene::setupLayer(
gfx::Context &ctx,
ox::Vector<uint16_t> const&layer,
unsigned layerNo) const noexcept {
gfx::setBgStatus(ctx, layerNo, true);
gfx::setBgCbb(ctx, layerNo, 0);
auto x = 0;
auto y = 0;
const auto width = m_sceneStatic.rows[layerNo];
for (auto const&tile : layer) {
const auto tile8 = static_cast<uint8_t>(tile);
gfx::setBgTile(ctx, layerNo, x, y, tile8);
gfx::setBgTile(ctx, layerNo, x + 1, y, tile8 + 1);
gfx::setBgTile(ctx, layerNo, x, y + 1, tile8 + 2);
gfx::setBgTile(ctx, layerNo, x + 1, y + 1, tile8 + 3);
x += 2;
if (x >= width * 2) {
x = 0;
y += 2;
}
}
}
}

View File

@ -1,20 +0,0 @@
add_library(
NostalgiaScene-Studio
studiomodule.cpp
sceneeditor-imgui.cpp
sceneeditor.cpp
sceneeditorview.cpp
)
target_link_libraries(
NostalgiaScene-Studio PUBLIC
NostalgiaScene
Studio
)
install(
TARGETS
NostalgiaScene-Studio
LIBRARY DESTINATION
${NOSTALGIA_DIST_MODULE}
)

View File

@ -1,55 +0,0 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include <imgui.h>
#include <keel/media.hpp>
#include "sceneeditor-imgui.hpp"
namespace nostalgia::scene {
SceneEditorImGui::SceneEditorImGui(studio::StudioContext &ctx, ox::StringView path):
Editor(ctx, path),
m_ctx(ctx.tctx),
m_editor(m_ctx, path),
m_view(m_ctx, m_editor.scene()) {
}
void SceneEditorImGui::draw(studio::StudioContext&) noexcept {
auto const paneSize = ImGui::GetContentRegionAvail();
m_view.draw(ox::Size{static_cast<int>(paneSize.x), static_cast<int>(paneSize.y)});
auto &fb = m_view.framebuffer();
auto const srcH = static_cast<float>(fb.height) / static_cast<float>(fb.width);
auto const dstH = paneSize.y / paneSize.x;
float xScale{}, yScale{};
if (dstH > srcH) {
// crop off width
xScale = srcH / dstH;
yScale = 1;
} else {
auto const srcW = static_cast<float>(fb.width) / static_cast<float>(fb.height);
auto const dstW = (paneSize.x / paneSize.y);
xScale = 1;
yScale = srcW / dstW;
}
uintptr_t const buffId = fb.color.id;
ImGui::Image(
std::bit_cast<void*>(buffId),
paneSize,
ImVec2(0, 1),
ImVec2(xScale, 1 - yScale));
}
void SceneEditorImGui::onActivated() noexcept {
oxLogError(m_view.setupScene());
}
ox::Error SceneEditorImGui::saveItem() noexcept {
const auto sctx = applicationData<studio::StudioContext>(m_ctx);
OX_RETURN_ERROR(sctx->project->writeObj(itemPath(), m_editor.scene()));
return {};
}
}

View File

@ -1,35 +0,0 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#pragma once
#include <studio/studio.hpp>
#include <turbine/context.hpp>
#include "sceneeditor.hpp"
#include "sceneeditorview.hpp"
namespace nostalgia::scene {
class SceneEditorImGui: public studio::Editor {
private:
turbine::Context &m_ctx;
SceneEditor m_editor;
SceneEditorView m_view;
public:
SceneEditorImGui(studio::StudioContext &ctx, ox::StringView path);
void draw(studio::StudioContext&) noexcept final;
void onActivated() noexcept override;
protected:
ox::Error saveItem() noexcept final;
};
}

View File

@ -1,16 +0,0 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include <keel/keel.hpp>
#include "sceneeditor.hpp"
namespace nostalgia::scene {
SceneEditor::SceneEditor(turbine::Context &ctx, ox::StringViewCR path):
m_ctx(ctx),
m_scene(*keel::readObj<SceneStatic>(keelCtx(m_ctx), path).unwrapThrow()) {
}
}

View File

@ -1,34 +0,0 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#pragma once
#include <turbine/context.hpp>
#include <nostalgia/scene/scene.hpp>
namespace nostalgia::scene {
class SceneEditor {
private:
turbine::Context &m_ctx;
ox::String m_itemName;
ox::String m_itemPath;
SceneStatic m_scene;
public:
SceneEditor(turbine::Context &ctx, ox::StringViewCR path);
[[nodiscard]]
SceneStatic const&scene() const noexcept {
return m_scene;
}
protected:
ox::Error saveItem() noexcept;
};
}

View File

@ -1,36 +0,0 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include <nostalgia/gfx/gfx.hpp>
#include "sceneeditorview.hpp"
namespace nostalgia::scene {
SceneEditorView::SceneEditorView(turbine::Context &tctx, SceneStatic const&sceneStatic):
m_cctx(gfx::init(tctx, {.glInstallDrawer = false}).unwrapThrow()),
m_sceneStatic(sceneStatic),
m_scene(m_sceneStatic) {
}
ox::Error SceneEditorView::setupScene() noexcept {
glutils::resizeInitFrameBuffer(m_frameBuffer, gfx::gl::drawSize(m_scale));
return m_scene.setupDisplay(*m_cctx);
}
void SceneEditorView::draw(ox::Size const&targetSz) noexcept {
auto const scaleSz = targetSz / gfx::gl::drawSize(1);
if (m_scaleSz != scaleSz) [[unlikely]] {
m_scale = ox::max(1, ox::max(scaleSz.width, scaleSz.height));
glutils::resizeInitFrameBuffer(m_frameBuffer, gfx::gl::drawSize(m_scale));
}
glutils::FrameBufferBind const frameBufferBind(m_frameBuffer);
gfx::gl::draw(*m_cctx, m_scale);
}
glutils::FrameBuffer const&SceneEditorView::framebuffer() const noexcept {
return m_frameBuffer;
}
}

View File

@ -1,37 +0,0 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#pragma once
#include <glutils/glutils.hpp>
#include <nostalgia/gfx/context.hpp>
#include <nostalgia/gfx/gfx.hpp>
#include <nostalgia/scene/scene.hpp>
namespace nostalgia::scene {
class SceneEditorView {
private:
ox::UPtr<gfx::Context> m_cctx;
SceneStatic const&m_sceneStatic;
Scene m_scene;
glutils::FrameBuffer m_frameBuffer;
int m_scale = 1;
ox::Size m_scaleSz = gfx::gl::drawSize(m_scale);
public:
SceneEditorView(turbine::Context &ctx, SceneStatic const&sceneStatic);
ox::Error setupScene() noexcept;
void draw(ox::Size const&targetSz) noexcept;
[[nodiscard]]
glutils::FrameBuffer const&framebuffer() const noexcept;
};
}

View File

@ -1,30 +0,0 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include <studio/studio.hpp>
#include "sceneeditor-imgui.hpp"
namespace nostalgia::scene {
constexpr ox::StringLiteral FileExt_nscn("nscn");
static class: public studio::Module {
public:
ox::Vector<studio::EditorMaker> editors(studio::StudioContext &ctx) const noexcept override {
return {
studio::editorMaker<SceneEditorImGui>(ctx, FileExt_nscn),
};
}
ox::Vector<ox::UPtr<studio::ItemMaker>> itemMakers(studio::StudioContext&) const noexcept override {
ox::Vector<ox::UPtr<studio::ItemMaker>> out;
return out;
}
} mod;
const studio::Module *studioModule() noexcept {
return &mod;
}
}

View File

@ -1,9 +1,8 @@
add_subdirectory(src) add_subdirectory(src)
target_include_directories( if(NOT BUILDCORE_TARGET STREQUAL "gba")
NostalgiaScene PUBLIC add_subdirectory(test)
include endif()
)
install( install(
DIRECTORY DIRECTORY

View File

@ -6,7 +6,7 @@
#include <keel/module.hpp> #include <keel/module.hpp>
namespace nostalgia::scene { namespace nostalgia::sound {
const keel::Module *keelModule() noexcept; const keel::Module *keelModule() noexcept;

View File

@ -0,0 +1,8 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#pragma once
namespace nostalgia::sound {
}

View File

@ -6,7 +6,7 @@
#include <studio/studio.hpp> #include <studio/studio.hpp>
namespace nostalgia::scene { namespace nostalgia::sound {
const studio::Module *studioModule() noexcept; const studio::Module *studioModule() noexcept;

View File

@ -1,18 +1,16 @@
add_library( add_library(
NostalgiaScene NostalgiaSound
scene.cpp sound.cpp
scenestatic.cpp
) )
target_include_directories( target_include_directories(
NostalgiaScene PUBLIC NostalgiaSound PUBLIC
../include ../include
) )
target_link_libraries( target_link_libraries(
NostalgiaScene PUBLIC NostalgiaSound PUBLIC
NostalgiaCore Turbine
) )
add_subdirectory(keel) add_subdirectory(keel)
@ -22,7 +20,7 @@ endif()
install( install(
TARGETS TARGETS
NostalgiaScene NostalgiaSound
DESTINATION DESTINATION
LIBRARY DESTINATION lib LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib ARCHIVE DESTINATION lib

View File

@ -0,0 +1,19 @@
add_library(
NostalgiaSound-Keel
keelmodule.cpp
typeconv.cpp
)
target_link_libraries(
NostalgiaSound-Keel PUBLIC
Keel
NostalgiaSound
)
install(
TARGETS
NostalgiaSound-Keel
DESTINATION
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)

View File

@ -0,0 +1,46 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include <ox/model/model.hpp>
#include <keel/module.hpp>
#include "typeconv.hpp"
namespace nostalgia::sound {
static class: public keel::Module {
private:
public:
[[nodiscard]]
ox::String id() const noexcept override {
return ox::String{"net.drinkingtea.nostalgia.sound"};
}
[[nodiscard]]
ox::Vector<keel::TypeDescGenerator> types() const noexcept final {
return {
};
}
[[nodiscard]]
ox::Vector<keel::BaseConverter const*> converters() const noexcept final {
return {
};
}
[[nodiscard]]
ox::Vector<keel::PackTransform> packTransforms() const noexcept final {
return {
};
}
} const mod;
keel::Module const*keelModule() noexcept {
return &mod;
}
}

View File

@ -2,10 +2,8 @@
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved. * Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/ */
#include <nostalgia/scene/scenestatic.hpp> #include "typeconv.hpp"
namespace nostalgia::scene {
namespace nostalgia::sound {
} }

View File

@ -0,0 +1,15 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#pragma once
#include <ox/std/def.hpp>
#include <keel/typeconv.hpp>
namespace nostalgia::sound {
// Type converters
}

View File

@ -0,0 +1,6 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
namespace nostalgia::sound {
}

View File

@ -0,0 +1,17 @@
add_library(
NostalgiaSound-Studio-ImGui
studiomodule.cpp
)
target_link_libraries(
NostalgiaSound-Studio-ImGui PUBLIC
NostalgiaSound
Studio
)
install(
TARGETS
NostalgiaSound-Studio-ImGui
LIBRARY DESTINATION
${NOSTALGIA_DIST_MODULE}
)

View File

@ -0,0 +1,27 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include <ox/std/memory.hpp>
#include <studio/studio.hpp>
namespace nostalgia::sound {
static class: public studio::Module {
ox::Vector<studio::EditorMaker> editors(studio::StudioContext&) const noexcept final {
return {
};
}
ox::Vector<ox::UPtr<studio::ItemMaker>> itemMakers(studio::StudioContext&) const noexcept final {
ox::Vector<ox::UniquePtr<studio::ItemMaker>> out;
return out;
}
} const mod;
const studio::Module *studioModule() noexcept {
return &mod;
}
}

View File

@ -0,0 +1,10 @@
add_executable(
NostalgiaSoundTest
tests.cpp
)
target_link_libraries(
NostalgiaSoundTest
NostalgiaSound
)

View File

@ -0,0 +1,26 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#undef NDEBUG
#include <map>
#include <ox/std/error.hpp>
#include <ox/mc/mc.hpp>
static std::map<ox::StringView, ox::Error(*)()> tests = {
};
int main(int argc, const char **argv) {
int retval = -1;
if (argc > 0) {
auto const args = ox::Span{argv, static_cast<size_t>(argc)};
auto const testName = ox::StringView(args[1]);
if (tests.find(testName) != tests.end()) {
retval = static_cast<int>(tests[testName]());
} else {
retval = 1;
}
}
return retval;
}

View File

@ -5,7 +5,7 @@
#include <studioapp/studioapp.hpp> #include <studioapp/studioapp.hpp>
#include <nostalgia/gfx/studiomodule.hpp> #include <nostalgia/gfx/studiomodule.hpp>
#include <nostalgia/scene/studiomodule.hpp> #include <nostalgia/sound/studiomodule.hpp>
namespace nostalgia { namespace nostalgia {
@ -16,7 +16,7 @@ void registerStudioModules() noexcept {
} }
modulesRegistered = true; modulesRegistered = true;
studio::registerModule(gfx::studioModule()); studio::registerModule(gfx::studioModule());
studio::registerModule(scene::studioModule()); studio::registerModule(sound::studioModule());
} }
} }

View File

@ -6,7 +6,6 @@
#include <turbine/turbine.hpp> #include <turbine/turbine.hpp>
#include <nostalgia/gfx/core.hpp> #include <nostalgia/gfx/core.hpp>
#include <nostalgia/scene/scene.hpp>
using namespace nostalgia; using namespace nostalgia;
@ -126,44 +125,13 @@ static ox::Error runTileSheetSetTest(turbine::Context &tctx) {
} }
static int sceneUpdateHandler(turbine::Context&) noexcept {
constexpr auto sleepTime = 16;
if (s_paused) {
return sleepTime;
}
// do stuff
return sleepTime;
}
static void sceneKeyEventHandler(turbine::Context &tctx, turbine::Key key, bool down) noexcept {
if (down) {
if (key == turbine::Key::Alpha_Q) {
turbine::requestShutdown(tctx);
} else if (key == turbine::Key::Alpha_P) {
s_paused = !s_paused;
}
}
}
[[maybe_unused]]
static ox::Error runScene(turbine::Context &tctx) {
constexpr ox::StringView SceneAddr{"/Scenes/Chester.nscn"};
OX_REQUIRE_M(cctx, gfx::init(tctx));
OX_REQUIRE(scn, keel::readObj<scene::SceneStatic>(keelCtx(tctx), SceneAddr));
turbine::setUpdateHandler(tctx, sceneUpdateHandler);
turbine::setKeyEventHandler(tctx, sceneKeyEventHandler);
scene::Scene const scene(*scn);
OX_RETURN_ERROR(scene.setupDisplay(*cctx));
return turbine::run(tctx);
}
ox::Error run( ox::Error run(
[[maybe_unused]] ox::StringView project, [[maybe_unused]] ox::StringView project,
[[maybe_unused]] ox::StringView appName, [[maybe_unused]] ox::StringView appName,
[[maybe_unused]] ox::StringView projectDataDir, [[maybe_unused]] ox::StringView projectDataDir,
ox::SpanView<char const*> args) noexcept { ox::SpanView<ox::CString> args) noexcept {
if (args.size() < 2) { if (args.size() < 2) {
return ox::Error(1, "Please provide path to project directory or OxFS file."); return ox::Error{1, "Please provide path to project directory or OxFS file."};
} }
auto const path = args[1]; auto const path = args[1];
OX_REQUIRE_M(fs, keel::loadRomFs(path)); OX_REQUIRE_M(fs, keel::loadRomFs(path));

View File

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

View File

@ -18,7 +18,7 @@
<string>APPL</string> <string>APPL</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>dev build</string> <string>d2025.02.0</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
<string>12.0.0</string> <string>12.0.0</string>

View File

@ -64,12 +64,16 @@ ox::Result<ox::CStringView> uuidToPath(Context &ctx, ox::UUID const&uuid) noexce
namespace detail { namespace detail {
template<typename T> template<typename T>
constexpr auto makeLoader(Context &ctx) { constexpr auto makeLoader(Context &ctx) {
return [&ctx](ox::StringViewCR assetId) -> ox::Result<T> { return [&ctx](ox::StringView assetId) -> ox::Result<T> {
if (!beginsWith(assetId, "/")) {
auto const p = ctx.uuidToPath.at(assetId); auto const p = ctx.uuidToPath.at(assetId);
if (p.error) { if (p.error) {
oxErrf("Could not find asset: {}", assetId);
return ox::Error{1, "Asset ID not found"}; return ox::Error{1, "Asset ID not found"};
} }
OX_REQUIRE(buff, ctx.rom->read(*p.value)); assetId = *p.value;
}
OX_REQUIRE(buff, ctx.rom->read(assetId));
auto [obj, err] = readAsset<T>(buff); auto [obj, err] = readAsset<T>(buff);
if (err) { if (err) {
if (err != ox::Error_ClawTypeVersionMismatch && err != ox::Error_ClawTypeMismatch) { if (err != ox::Error_ClawTypeVersionMismatch && err != ox::Error_ClawTypeMismatch) {

View File

@ -1,17 +1,17 @@
add_library( add_library(
StudioAppLib StudioAppLib
aboutpopup.cpp aboutpopup.cpp
app.cpp
clawviewer.cpp clawviewer.cpp
deleteconfirmation.cpp deleteconfirmation.cpp
filedialogmanager.cpp filedialogmanager.cpp
main.cpp
makecopypopup.cpp makecopypopup.cpp
newdir.cpp newdir.cpp
newmenu.cpp newmenu.cpp
newproject.cpp newproject.cpp
projectexplorer.cpp projectexplorer.cpp
renamefile.cpp renamefile.cpp
studioapp.cpp studioui.cpp
) )
target_compile_definitions( target_compile_definitions(
StudioAppLib PUBLIC StudioAppLib PUBLIC

View File

@ -11,8 +11,7 @@
#include <turbine/turbine.hpp> #include <turbine/turbine.hpp>
#include <studio/context.hpp> #include <studio/context.hpp>
#include <studioapp/studioapp.hpp> #include "studioui.hpp"
#include "studioapp.hpp"
namespace studio { namespace studio {

View File

@ -14,10 +14,14 @@
#include <studio/configio.hpp> #include <studio/configio.hpp>
#include "clawviewer.hpp" #include "clawviewer.hpp"
#include "filedialogmanager.hpp" #include "filedialogmanager.hpp"
#include "studioapp.hpp" #include "studioui.hpp"
namespace studio { namespace studio {
void navigateTo(StudioContext &ctx, ox::StringParam filePath, ox::StringParam navArgs) noexcept {
ctx.ui.navigateTo(std::move(filePath), std::move(navArgs));
}
namespace ig { namespace ig {
extern bool s_mainWinHasFocus; extern bool s_mainWinHasFocus;
} }
@ -107,6 +111,10 @@ void StudioUI::handleKeyEvent(turbine::Key const key, bool const down) noexcept
} }
} }
void StudioUI::navigateTo(ox::StringParam path, ox::StringParam navArgs) noexcept {
m_navAction.emplace(std::move(path), std::move(navArgs));
}
void StudioUI::draw() noexcept { void StudioUI::draw() noexcept {
glutils::clearScreen(); glutils::clearScreen();
drawMenu(); drawMenu();
@ -148,7 +156,7 @@ void StudioUI::draw() noexcept {
void StudioUI::drawMenu() noexcept { void StudioUI::drawMenu() noexcept {
if (ImGui::BeginMainMenuBar()) { if (ImGui::BeginMainMenuBar()) {
if (ImGui::BeginMenu("File")) { if (ImGui::BeginMenu("File")) {
if (ImGui::MenuItem("New...", "Ctrl+N")) { if (ImGui::MenuItem("New...", "Ctrl+N", false, m_project)) {
m_newMenu.open(); m_newMenu.open();
} }
if (ImGui::MenuItem("New Project...", "Ctrl+Shift+N")) { if (ImGui::MenuItem("New Project...", "Ctrl+Shift+N")) {
@ -277,6 +285,11 @@ void StudioUI::drawTabs() noexcept {
} }
m_closeActiveTab = false; m_closeActiveTab = false;
} }
if (m_navAction) {
oxLogError(openFile(m_navAction->path));
m_activeEditor->navigateTo(m_navAction->args);
m_navAction.reset();
}
} }
void StudioUI::loadEditorMaker(EditorMaker const&editorMaker) noexcept { void StudioUI::loadEditorMaker(EditorMaker const&editorMaker) noexcept {

View File

@ -62,12 +62,19 @@ class StudioUI: public ox::SignalHandler {
&m_renameFile, &m_renameFile,
}; };
bool m_showProjectExplorer = true; bool m_showProjectExplorer = true;
struct NavAction {
ox::String path;
ox::String args;
};
ox::Optional<NavAction> m_navAction;
public: public:
explicit StudioUI(turbine::Context &ctx, ox::StringParam projectDataDir) noexcept; explicit StudioUI(turbine::Context &ctx, ox::StringParam projectDataDir) noexcept;
void handleKeyEvent(turbine::Key, bool down) noexcept; void handleKeyEvent(turbine::Key, bool down) noexcept;
void navigateTo(ox::StringParam path, ox::StringParam navArgs) noexcept;
[[nodiscard]] [[nodiscard]]
constexpr Project *project() noexcept { constexpr Project *project() noexcept {
return m_project.get(); return m_project.get();

View File

@ -27,4 +27,6 @@ inline keel::Context &keelCtx(StudioContext &ctx) noexcept {
return keelCtx(ctx.tctx); return keelCtx(ctx.tctx);
} }
void navigateTo(StudioContext &ctx, ox::StringParam filePath, ox::StringParam navArgs) noexcept;
} }

View File

@ -99,6 +99,8 @@ class BaseEditor: public Widget {
[[nodiscard]] [[nodiscard]]
virtual UndoStack *undoStack() noexcept; virtual UndoStack *undoStack() noexcept;
virtual void navigateTo(ox::StringViewCR arg) noexcept;
void setRequiresConstantRefresh(bool value) noexcept; void setRequiresConstantRefresh(bool value) noexcept;
// signals // signals

View File

@ -9,6 +9,7 @@
#include <studio/dragdrop.hpp> #include <studio/dragdrop.hpp>
#include <studio/editor.hpp> #include <studio/editor.hpp>
#include <studio/filedialog.hpp> #include <studio/filedialog.hpp>
#include <studio/filepickerpopup.hpp>
#include <studio/filetreemodel.hpp> #include <studio/filetreemodel.hpp>
#include <studio/imguiutil.hpp> #include <studio/imguiutil.hpp>
#include <studio/module.hpp> #include <studio/module.hpp>

View File

@ -44,5 +44,5 @@ install(
DIRECTORY DIRECTORY
../include/studio ../include/studio
DESTINATION DESTINATION
include/studio/ include/studio
) )

View File

@ -106,6 +106,8 @@ UndoStack *BaseEditor::undoStack() noexcept {
return nullptr; return nullptr;
} }
void BaseEditor::navigateTo(ox::StringViewCR) noexcept {}
Editor::Editor(StudioContext &ctx, ox::StringParam itemPath) noexcept: Editor::Editor(StudioContext &ctx, ox::StringParam itemPath) noexcept:
m_itemPath(std::move(itemPath)), m_itemPath(std::move(itemPath)),

View File

@ -84,10 +84,11 @@ ox::FileSystem &Project::romFs() noexcept {
} }
ox::Error Project::mkdir(ox::StringViewCR path) const noexcept { ox::Error Project::mkdir(ox::StringViewCR path) const noexcept {
auto const [stat, err] = m_fs.stat(path); auto [stat, err] = m_fs.stat(path);
if (err) { if (err) {
OX_RETURN_ERROR(m_fs.mkdir(path, true)); OX_RETURN_ERROR(m_fs.mkdir(path, true));
dirAdded.emit(path); dirAdded.emit(path);
OX_RETURN_ERROR(m_fs.stat(path).moveTo(stat));
} }
return stat.fileType == ox::FileType::Directory ? return stat.fileType == ox::FileType::Directory ?
ox::Error{} : ox::Error{1, "path exists as normal file"}; ox::Error{} : ox::Error{1, "path exists as normal file"};

View File

@ -7,9 +7,7 @@
namespace studio { namespace studio {
ox::Error UndoStack::push(ox::UPtr<UndoCommand> &&cmd) noexcept { ox::Error UndoStack::push(ox::UPtr<UndoCommand> &&cmd) noexcept {
for (auto const i = m_stackIdx; i < m_stack.size();) { m_stack.resize(m_stackIdx);
std::ignore = m_stack.erase(i);
}
OX_RETURN_ERROR(cmd->redo()); OX_RETURN_ERROR(cmd->redo());
redoTriggered.emit(cmd.get()); redoTriggered.emit(cmd.get());
changeTriggered.emit(cmd.get()); changeTriggered.emit(cmd.get());
@ -25,22 +23,29 @@ ox::Error UndoStack::push(ox::UPtr<UndoCommand> &&cmd) noexcept {
} }
ox::Error UndoStack::redo() noexcept { ox::Error UndoStack::redo() noexcept {
if (m_stackIdx < m_stack.size()) { while (m_stackIdx < m_stack.size()) {
auto &c = m_stack[m_stackIdx]; auto const &c = m_stack[m_stackIdx];
OX_RETURN_ERROR(c->redo());
++m_stackIdx; ++m_stackIdx;
if (!c->isObsolete()) {
OX_RETURN_ERROR(c->redo());
redoTriggered.emit(c.get()); redoTriggered.emit(c.get());
changeTriggered.emit(c.get()); changeTriggered.emit(c.get());
break;
}
} }
return {}; return {};
} }
ox::Error UndoStack::undo() noexcept { ox::Error UndoStack::undo() noexcept {
if (m_stackIdx) { while (m_stackIdx) {
auto &c = m_stack[--m_stackIdx]; --m_stackIdx;
auto const &c = m_stack[m_stackIdx];
if (!c->isObsolete()) {
OX_RETURN_ERROR(c->undo()); OX_RETURN_ERROR(c->undo());
undoTriggered.emit(c.get()); undoTriggered.emit(c.get());
changeTriggered.emit(c.get()); changeTriggered.emit(c.get());
break;
}
} }
return {}; return {};
} }