Nostalgia Studio d2025.05.0 was released on May 10, 2025.
Release Notes
- Add app icon for both window and file
- Change application font to Roboto Medium
- Closing application will now confirm with user if any files have unsaved changes.
- UUID duplicates will now be reported when opening a project
- Deleting a directory now closes files in that directory
- Delete key now initiates deletion of selected directory
- Remove ability to re-order tabs. There were bugs associated with that.
- Mac: Menu bar shortcuts now say Cmd instead of Ctrl.
- TileSheetEditor: Fix selection clearing to work when clicking outside image.
- TileSheetEditor: Fix Delete Tile functionality, which was completely broken
- TileSheetEditor: Fix Insert Tile functionality, which was completely broken
- PaletteEditor: Fix color number key range. Previously, pressing A caused the editor to jump to the last color.
- PaletteEditor: Page rename will now take effect upon pressing enter if the text input has focus
Novel Code Changes
This section is completely useless for a user, but just highlights some fun internal changes made in this release.
This release simplifies type converters.
Here is an old converter:
class PaletteToCompactPaletteConverter:
public keel::Converter<Palette, CompactPalette> {
ox::Error convert(
keel::Context&, Palette &src, CompactPalette &dst) const noexcept final;
};
ox::Error PaletteToCompactPaletteConverter::convert(
keel::Context&,
Palette &src,
CompactPalette &dst) const noexcept {
dst.pages.reserve(src.pages.size());
for (auto &page : src.pages) {
auto &p = dst.pages.emplace_back();
for (auto const c : page.colors) {
p.emplace_back(c);
}
}
return {};
}
That converter is now simply a function:
ox::Error convertPaletteToCompactPalette(
keel::Context&,
Palette &src,
CompactPalette &dst) noexcept {
dst.pages.reserve(src.pages.size());
for (auto &page : src.pages) {
auto &p = dst.pages.emplace_back();
for (auto const c : page.colors) {
p.emplace_back(c);
}
}
return {};
}
The converter registration code has also changed accordingly.
The old system required instantiating them apart from their registration, as seen below.
static class: public keel::Module {
private:
NostalgiaPaletteToPaletteV1Converter m_nostalgiaPaletteToPaletteV1Converter;
PaletteV1ToPaletteV2Converter m_paletteV1ToPaletteV2Converter;
PaletteV2ToPaletteV3Converter m_paletteV2ToPaletteV3Converter;
PaletteV3ToPaletteV4Converter m_paletteV3ToPaletteV4Converter;
PaletteV4ToPaletteV5Converter m_paletteV4ToPaletteV5Converter;
PaletteToCompactPaletteConverter m_paletteToCompactPaletteConverter;
TileSheetV1ToTileSheetV2Converter m_tileSheetV1ToTileSheetV2Converter;
TileSheetV2ToTileSheetV3Converter m_tileSheetV2ToTileSheetV3Converter;
TileSheetV3ToTileSheetV4Converter m_tileSheetV3ToTileSheetV4Converter;
TileSheetV4ToTileSheetV5Converter m_tileSheetV4ToTileSheetV5Converter;
TileSheetToCompactTileSheetConverter m_tileSheetToCompactTileSheetConverter;
public:
...
[[nodiscard]]
ox::Vector<keel::BaseConverter const*> converters() const noexcept final {
return {
&m_nostalgiaPaletteToPaletteV1Converter,
&m_paletteV1ToPaletteV2Converter,
&m_paletteV2ToPaletteV3Converter,
&m_paletteV3ToPaletteV4Converter,
&m_paletteV4ToPaletteV5Converter,
&m_paletteToCompactPaletteConverter,
&m_tileSheetV1ToTileSheetV2Converter,
&m_tileSheetV2ToTileSheetV3Converter,
&m_tileSheetV3ToTileSheetV4Converter,
&m_tileSheetV4ToTileSheetV5Converter,
&m_tileSheetToCompactTileSheetConverter,
};
}
...
} const mod;
The new system removes that need.
static class: public keel::Module {
public:
...
[[nodiscard]]
ox::Vector<keel::Converter> converters() const noexcept final {
return {
keel::Converter::make<convertNostalgiaPaletteToPaletteV1>(),
keel::Converter::make<convertPaletteV1ToPaletteV2>(),
keel::Converter::make<convertPaletteV2ToPaletteV3>(),
keel::Converter::make<convertPaletteV3ToPaletteV4>(),
keel::Converter::make<convertPaletteV4ToPaletteV5>(),
keel::Converter::make<convertPaletteToCompactPalette>(),
keel::Converter::make<convertTileSheetV1ToTileSheetV2>(),
keel::Converter::make<convertTileSheetV2ToTileSheetV3>(),
keel::Converter::make<convertTileSheetV3ToTileSheetV4>(),
keel::Converter::make<convertTileSheetV4ToTileSheetV5>(),
keel::Converter::make<convertTileSheetToCompactTileSheet>(),
};
}
...
} const mod;
The innards of this new system largely build upon the existing converter system, but the new Converter type is basically a wrapper around the existing system.
class Converter {
private:
ox::AllocAlias<BaseConverter> m_buff{};
public:
template<auto Func>
static Converter make() noexcept {
Converter out;
static_assert(sizeof(ConverterFunc<Func>) <= sizeof(out.m_buff));
new (out.m_buff.data()) ConverterFunc<Func>{};
return out;
}
[[nodiscard]]
BaseConverter const &converter() const noexcept {
return *m_buff.data();
}
};
The new ConverterFunc class, that is utilized in Converter above, hooks into the existing system. As seen below, it uses some fun templating tricks to extract the argument types of the parameters, and is thus able to report the operand types to the conversion system.
template<auto Func>
class ConverterFunc final: public BaseConverter {
private:
template<typename SrcType, typename DstType>
struct ParamPack {
using Src = SrcType;
using Dst = DstType;
};
template<typename Src, typename Dst>
static ParamPack<Src, Dst> extractParams(
ox::Error (*)(Context&, Src&, Dst&)) {
return {};
}
public:
using SrcType = typename decltype(extractParams(Func))::Src;
using DstType = typename decltype(extractParams(Func))::Dst;
...
};