Compare commits

..

1 Commits

Author SHA1 Message Date
0ecb98769d [nostalgia/studio] Update version to d2024.12.0
All checks were successful
Build / build (push) Successful in 3m18s
2025-01-11 17:09:20 -06:00
20 changed files with 170 additions and 278 deletions

View File

@@ -58,11 +58,7 @@ class TypeStore {
if (!std::is_constant_evaluated()) { if (!std::is_constant_evaluated()) {
OX_REQUIRE_M(dt, loadDescriptor(typeId)); OX_REQUIRE_M(dt, loadDescriptor(typeId));
for (auto &f : dt->fieldList) { for (auto &f : dt->fieldList) {
if (typeId == f.typeId) { OX_RETURN_ERROR(this->getLoad(f.typeId).moveTo(f.type));
f.type = dt.get();
} else {
OX_RETURN_ERROR(this->getLoad(f.typeId).moveTo(f.type));
}
} }
auto &out = m_cache[typeId]; auto &out = m_cache[typeId];
out = std::move(dt); out = std::move(dt);

View File

@@ -11,7 +11,6 @@
#include "algorithm.hpp" #include "algorithm.hpp"
#include "hash.hpp" #include "hash.hpp"
#include "ignore.hpp" #include "ignore.hpp"
#include "optional.hpp"
#include "stringview.hpp" #include "stringview.hpp"
#include "strops.hpp" #include "strops.hpp"
#include "vector.hpp" #include "vector.hpp"
@@ -27,12 +26,11 @@ class HashMap {
private: private:
struct Pair { struct Pair {
UPtr<Pair> next;
K key = {}; K key = {};
T value{}; T value{};
}; };
Vector<K> m_keys; Vector<K> m_keys;
Vector<UPtr<Pair>> m_pairs; Vector<Pair*> m_pairs;
public: public:
explicit constexpr HashMap(std::size_t size = 127); explicit constexpr HashMap(std::size_t size = 127);
@@ -75,10 +73,10 @@ class HashMap {
constexpr void expand(); constexpr void expand();
template<typename KK> template<typename KK>
constexpr UPtr<Pair> const &access(Vector<UPtr<Pair>> const &pairs, KK const &key) const; constexpr Pair *const&access(Vector<Pair*> const&pairs, KK const&key) const;
template<typename KK> template<typename KK>
constexpr UPtr<Pair> &access(Vector<UPtr<Pair>> &pairs, KK const &key); constexpr Pair *&access(Vector<Pair*> &pairs, KK const&key);
}; };
@@ -87,13 +85,14 @@ constexpr HashMap<K, T>::HashMap(std::size_t size): m_pairs(size) {
} }
template<typename K, typename T> template<typename K, typename T>
constexpr HashMap<K, T>::HashMap(HashMap const &other) { constexpr HashMap<K, T>::HashMap(HashMap<K, T> const&other) {
operator=(other); m_pairs = other.m_pairs;
} }
template<typename K, typename T> template<typename K, typename T>
constexpr HashMap<K, T>::HashMap(HashMap &&other) noexcept { constexpr HashMap<K, T>::HashMap(HashMap<K, T> &&other) noexcept {
operator=(std::move(other)); m_keys = std::move(other.m_keys);
m_pairs = std::move(other.m_pairs);
} }
template<typename K, typename T> template<typename K, typename T>
@@ -102,7 +101,7 @@ constexpr HashMap<K, T>::~HashMap() {
} }
template<typename K, typename T> template<typename K, typename T>
constexpr bool HashMap<K, T>::operator==(HashMap const &other) const { constexpr bool HashMap<K, T>::operator==(HashMap const&other) const {
if (m_keys != other.m_keys) { if (m_keys != other.m_keys) {
return false; return false;
} }
@@ -116,25 +115,19 @@ constexpr bool HashMap<K, T>::operator==(HashMap const &other) const {
} }
template<typename K, typename T> template<typename K, typename T>
constexpr HashMap<K, T> &HashMap<K, T>::operator=(HashMap const &other) { constexpr HashMap<K, T> &HashMap<K, T>::operator=(HashMap<K, T> const&other) {
if (this != &other) { if (this != &other) {
clear(); clear();
m_keys = other.m_keys; m_keys = other.m_keys;
m_pairs.resize(other.m_pairs.size()); m_pairs = other.m_pairs;
for (auto const&k : m_keys) {
auto const &src = access(other.m_pairs, k);
auto &dst = access(m_pairs, k);
dst = ox::make_unique<Pair>();
dst->key = src->key;
dst->value = src->value;
}
} }
return *this; return *this;
} }
template<typename K, typename T> template<typename K, typename T>
constexpr HashMap<K, T> &HashMap<K, T>::operator=(HashMap &&other) noexcept { constexpr HashMap<K, T> &HashMap<K, T>::operator=(HashMap<K, T> &&other) noexcept {
if (this != &other) { if (this != &other) {
clear();
m_keys = std::move(other.m_keys); m_keys = std::move(other.m_keys);
m_pairs = std::move(other.m_pairs); m_pairs = std::move(other.m_pairs);
} }
@@ -142,52 +135,60 @@ constexpr HashMap<K, T> &HashMap<K, T>::operator=(HashMap &&other) noexcept {
} }
template<typename K, typename T> template<typename K, typename T>
constexpr T &HashMap<K, T>::operator[](MaybeView_t<K> const &key) { constexpr T &HashMap<K, T>::operator[](MaybeView_t<K> const&k) {
auto p = &access(m_pairs, key); auto p = &access(m_pairs, k);
if (*p == nullptr) { if (*p == nullptr) {
if (static_cast<double>(m_pairs.size()) * 0.7 < if (static_cast<double>(m_pairs.size()) * 0.7 <
static_cast<double>(m_keys.size())) { static_cast<double>(m_keys.size())) {
expand(); expand();
p = &access(m_pairs, key); p = &access(m_pairs, k);
} }
*p = ox::make_unique<Pair>(); *p = new Pair;
(*p)->key = key; (*p)->key = k;
m_keys.emplace_back(key); m_keys.emplace_back(k);
} }
return (*p)->value; return (*p)->value;
} }
template<typename K, typename T> template<typename K, typename T>
constexpr Result<T*> HashMap<K, T>::at(MaybeView_t<K> const &key) noexcept { constexpr Result<T*> HashMap<K, T>::at(MaybeView_t<K> const&k) noexcept {
auto &p = access(m_pairs, key); auto p = access(m_pairs, k);
if (!p) { if (!p) {
return ox::Error{1, "value not found for given key"}; return {nullptr, ox::Error(1, "value not found for given key")};
} }
return &p->value; return &p->value;
} }
template<typename K, typename T> template<typename K, typename T>
constexpr Result<const T*> HashMap<K, T>::at(MaybeView_t<K> const &key) const noexcept { constexpr Result<const T*> HashMap<K, T>::at(MaybeView_t<K> const&k) const noexcept {
auto &p = access(m_pairs, key); auto p = access(m_pairs, k);
if (!p) { if (!p) {
return ox::Error{1, "value not found for given key"}; return {nullptr, ox::Error(1, "value not found for given key")};
} }
return &p->value; return &p->value;
} }
template<typename K, typename T> template<typename K, typename T>
constexpr void HashMap<K, T>::erase(MaybeView_t<K> const &key) { constexpr void HashMap<K, T>::erase(MaybeView_t<K> const&k) {
if (!contains(key)) { if (!contains(k)) {
return; return;
} }
auto &c = access(m_pairs, key); auto h = ox::hash<MaybeView_t<K>>{}(k) % m_pairs.size();
c = std::move(c->next); while (true) {
std::ignore = m_keys.erase(ox::find(m_keys.begin(), m_keys.end(), key)); const auto &p = m_pairs[h];
if (p == nullptr || p->key == k) {
std::ignore = m_pairs.erase(h);
break;
} else {
h = ox::hash<MaybeView_t<K>>{}(k) % m_pairs.size();
}
}
std::ignore = m_keys.erase(ox::find(m_keys.begin(), m_keys.end(), k));
} }
template<typename K, typename T> template<typename K, typename T>
constexpr bool HashMap<K, T>::contains(MaybeView_t<K> const &key) const noexcept { constexpr bool HashMap<K, T>::contains(MaybeView_t<K> const&k) const noexcept {
return access(m_pairs, key).get() != nullptr; return access(m_pairs, k) != nullptr;
} }
template<typename K, typename T> template<typename K, typename T>
@@ -203,26 +204,27 @@ constexpr Vector<K> const&HashMap<K, T>::keys() const noexcept {
template<typename K, typename T> template<typename K, typename T>
constexpr Vector<T> HashMap<K, T>::values() const noexcept { constexpr Vector<T> HashMap<K, T>::values() const noexcept {
Vector<T> out; Vector<T> out;
out.reserve(m_keys.size()); out.reserve(m_pairs.size());
for (auto const &p : m_pairs) { for (auto const&p : m_pairs) {
if (out) { out.emplace_back(p->value);
out.emplace_back(p->value);
}
} }
return out; return out;
} }
template<typename K, typename T> template<typename K, typename T>
constexpr void HashMap<K, T>::clear() { constexpr void HashMap<K, T>::clear() {
for (std::size_t i = 0; i < m_pairs.size(); i++) {
delete m_pairs[i];
}
m_pairs.clear(); m_pairs.clear();
m_pairs.resize(127); m_pairs.resize(127);
} }
template<typename K, typename T> template<typename K, typename T>
constexpr void HashMap<K, T>::expand() { constexpr void HashMap<K, T>::expand() {
Vector<UPtr<Pair>> r{m_pairs.size() * 2}; Vector<Pair*> r(m_pairs.size() * 2);
for (std::size_t i = 0; i < m_keys.size(); ++i) { for (std::size_t i = 0; i < m_keys.size(); ++i) {
auto const &k = m_keys[i]; auto const&k = m_keys[i];
access(r, k) = std::move(access(m_pairs, k)); access(r, k) = std::move(access(m_pairs, k));
} }
m_pairs = std::move(r); m_pairs = std::move(r);
@@ -230,39 +232,29 @@ constexpr void HashMap<K, T>::expand() {
template<typename K, typename T> template<typename K, typename T>
template<typename KK> template<typename KK>
constexpr UPtr<typename HashMap<K, T>::Pair> const &HashMap<K, T>::access( constexpr typename HashMap<K, T>::Pair *const&HashMap<K, T>::access(Vector<Pair*> const&pairs, KK const&k) const {
Vector<UPtr<Pair>> const& pairs, auto h = static_cast<std::size_t>(ox::hash<KK>{}(k) % pairs.size());
KK const &key) const {
auto const h = static_cast<std::size_t>(ox::hash<KK>{}(key) % pairs.size());
auto const &p = *pairs.at(h).unwrap();
if (p == nullptr || p->key == key) {
return p;
}
auto c = &p->next;
while (true) { while (true) {
if (*c == nullptr || (*c)->key == key) { auto const&p = *pairs.at(h).unwrap();
return *c; if (p == nullptr || p->key == k) {
return p;
} else {
h = (h + 1) % pairs.size();
} }
c = &(*c)->next;
} }
} }
template<typename K, typename T> template<typename K, typename T>
template<typename KK> template<typename KK>
constexpr UPtr<typename HashMap<K, T>::Pair> &HashMap<K, T>::access( constexpr typename HashMap<K, T>::Pair *&HashMap<K, T>::access(Vector<Pair*> &pairs, KK const&k) {
Vector<UPtr<Pair>> &pairs, auto h = static_cast<std::size_t>(ox::hash<KK>{}(k) % pairs.size());
KK const &key) {
auto const h = static_cast<std::size_t>(ox::hash<KK>{}(key) % pairs.size());
auto &p = *pairs.at(h).unwrap();
if (p == nullptr || p->key == key) {
return p;
}
auto c = &p->next;
while (true) { while (true) {
if (*c == nullptr || (*c)->key == key) { auto &p = *pairs.at(h).unwrap();
return *c; if (p == nullptr || p->key == k) {
return p;
} else {
h = (h + 1) % pairs.size();
} }
c = &(*c)->next;
} }
} }

View File

@@ -260,12 +260,12 @@ constexpr bool operator==(const UniquePtr<T> &p1, const UniquePtr<T> &p2) noexce
template<typename T> template<typename T>
constexpr bool operator==(const UniquePtr<T> &p1, std::nullptr_t) noexcept { constexpr bool operator==(const UniquePtr<T> &p1, std::nullptr_t) noexcept {
return p1.get() == nullptr; return p1.get();
} }
template<typename T> template<typename T>
constexpr bool operator==(std::nullptr_t, const UniquePtr<T> &p2) noexcept { constexpr bool operator==(std::nullptr_t, const UniquePtr<T> &p2) noexcept {
return p2.get() == nullptr; return p2.get();
} }

View File

@@ -392,7 +392,7 @@ template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v> BasicString<SmallStringSize_v>::operator+(const char *str) const noexcept { constexpr BasicString<SmallStringSize_v> BasicString<SmallStringSize_v>::operator+(const char *str) const noexcept {
const std::size_t strLen = ox::strlen(str); const std::size_t strLen = ox::strlen(str);
const auto currentLen = len(); const auto currentLen = len();
BasicString<SmallStringSize_v> cpy; BasicString<SmallStringSize_v> cpy(currentLen + strLen);
cpy.m_buff.resize(m_buff.size() + strLen); cpy.m_buff.resize(m_buff.size() + strLen);
ox::listcpy(&cpy.m_buff[0], m_buff.data(), currentLen); ox::listcpy(&cpy.m_buff[0], m_buff.data(), currentLen);
ox::listcpy(&cpy.m_buff[currentLen], str, strLen); ox::listcpy(&cpy.m_buff[currentLen], str, strLen);
@@ -425,8 +425,7 @@ constexpr BasicString<SmallStringSize_v> BasicString<SmallStringSize_v>::operato
BasicString<SmallStringSize_v> cpy(currentLen + strLen); BasicString<SmallStringSize_v> cpy(currentLen + strLen);
cpy.m_buff.resize(m_buff.size() + strLen); cpy.m_buff.resize(m_buff.size() + strLen);
ox::listcpy(&cpy.m_buff[0], m_buff.data(), currentLen); ox::listcpy(&cpy.m_buff[0], m_buff.data(), currentLen);
ox::listcpy(&cpy.m_buff[currentLen], src.data(), strLen); ox::listcpy(&cpy.m_buff[currentLen], src.data(), strLen + 1);
cpy.m_buff[cpy.m_buff.size() - 1] = 0;
return cpy; return cpy;
} }
@@ -437,8 +436,7 @@ constexpr BasicString<SmallStringSize_v> BasicString<SmallStringSize_v>::operato
BasicString<SmallStringSize_v> cpy(currentLen + strLen); BasicString<SmallStringSize_v> cpy(currentLen + strLen);
cpy.m_buff.resize(m_buff.size() + strLen); cpy.m_buff.resize(m_buff.size() + strLen);
ox::listcpy(&cpy.m_buff[0], m_buff.data(), currentLen); ox::listcpy(&cpy.m_buff[0], m_buff.data(), currentLen);
ox::listcpy(&cpy.m_buff[currentLen], src.data(), strLen); ox::listcpy(&cpy.m_buff[currentLen], src.data(), strLen + 1);
cpy.m_buff[cpy.m_buff.size() - 1] = 0;
return cpy; return cpy;
} }

View File

@@ -273,16 +273,6 @@ OX_CLANG_NOWARN_END
si["aoeu"] = 100; si["aoeu"] = 100;
oxAssert(si["asdf"] == 42, "asdf != 42"); oxAssert(si["asdf"] == 42, "asdf != 42");
oxAssert(si["aoeu"] == 100, "aoeu != 100"); oxAssert(si["aoeu"] == 100, "aoeu != 100");
si.erase("asdf");
oxAssert(!si.contains("asdf"), "wrongly contains asdf");
oxAssert(si.contains("aoeu"), "does not contains aoeu");
oxAssert(!si.at("asdf").ok(), "asdf != 0");
oxExpect(si["asdf"], 0);
oxAssert(si["aoeu"] == 100, "aoeu != 100");
auto si2 = si;
oxDebugf("{}", si2["asdf"]);
oxExpect(si2["asdf"], 0);
oxAssert(si2["aoeu"] == 100, "aoeu != 100");
ox::HashMap<int, int> ii; ox::HashMap<int, int> ii;
ii[4] = 42; ii[4] = 42;
ii[5] = 100; ii[5] = 100;

View File

@@ -233,12 +233,10 @@ 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 return ox::all_of(ss.subsheets.begin(), ss.subsheets.end(),
(ss.pixels.empty() || ss.subsheets.empty()) && [bpp, bytes](TileSheetV4::SubSheet const&s) {
ox::all_of(ss.subsheets.begin(), ss.subsheets.end(), return bytes == s.pixels.size() && valid(s, bpp);
[bpp, bytes](TileSheetV4::SubSheet const&s) { });
return bytes == s.pixels.size() && valid(s, bpp);
});
} }
[[nodiscard]] [[nodiscard]]
@@ -247,14 +245,8 @@ 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 {
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);
} 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

@@ -1,7 +1,6 @@
target_sources( target_sources(
NostalgiaCore-Studio PRIVATE NostalgiaCore-Studio PRIVATE
commands/addcolorcommand.cpp commands/addcolorcommand.cpp
commands/addpagecommand.cpp
commands/applycolorallpagescommand.cpp commands/applycolorallpagescommand.cpp
commands/duplicatepagecommand.cpp commands/duplicatepagecommand.cpp
commands/movecolorcommand.cpp commands/movecolorcommand.cpp

View File

@@ -1,28 +0,0 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include "commands.hpp"
#include "addpagecommand.hpp"
namespace nostalgia::core {
AddPageCommand::AddPageCommand(Palette &pal) noexcept:
m_pal(pal) {}
int AddPageCommand::commandId() const noexcept {
return static_cast<int>(PaletteEditorCommandId::AddPage);
}
ox::Error AddPageCommand::redo() noexcept {
m_pal.pages.emplace_back(ox::sfmt("Page {}", m_pal.pages.size() + 1), ox::Vector<PaletteColor>{});
return {};
}
ox::Error AddPageCommand::undo() noexcept {
m_pal.pages.pop_back();
return {};
}
}

View File

@@ -1,31 +0,0 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#pragma once
#include <studio/studio.hpp>
#include <nostalgia/core/palette.hpp>
namespace nostalgia::core {
class AddPageCommand: public studio::UndoCommand {
private:
Palette &m_pal;
public:
explicit AddPageCommand(Palette &pal) noexcept;
~AddPageCommand() noexcept override = default;
[[nodiscard]]
int commandId() const noexcept final;
ox::Error redo() noexcept final;
ox::Error undo() noexcept final;
};
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -234,7 +234,7 @@ void TileSheetEditorImGui::draw(studio::StudioContext&) noexcept {
ImGui::BeginChild("SubSheets", {s_palViewWidth - 24, ySize / 2.f}, true); ImGui::BeginChild("SubSheets", {s_palViewWidth - 24, ySize / 2.f}, true);
{ {
static constexpr auto btnHeight = ig::BtnSz.y; static constexpr auto btnHeight = ig::BtnSz.y;
auto constexpr btnSize = ImVec2{btnHeight, btnHeight}; auto const btnSize = ImVec2{btnHeight, btnHeight};
if (ig::PushButton("+", btnSize)) { if (ig::PushButton("+", btnSize)) {
auto insertOnIdx = m_model.activeSubSheetIdx(); auto insertOnIdx = m_model.activeSubSheetIdx();
auto const&parent = m_model.activeSubSheet(); auto const&parent = m_model.activeSubSheet();
@@ -258,19 +258,16 @@ void TileSheetEditorImGui::draw(studio::StudioContext&) noexcept {
m_exportMenu.show(); m_exportMenu.show();
} }
TileSheet::SubSheetIdx path; TileSheet::SubSheetIdx path;
static constexpr auto flags = static constexpr auto flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody;
ImGuiTableFlags_RowBg | if (ImGui::BeginTable("Subsheets", 4, flags)) {
ImGuiTableFlags_NoBordersInBody | ImGui::TableSetupColumn("Subsheet", ImGuiTableColumnFlags_NoHide);
ImGuiTableFlags_ScrollY; ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 25);
if (ImGui::BeginTable("Subsheets", 4, flags)) { ImGui::TableSetupColumn("Columns", ImGuiTableColumnFlags_WidthFixed, 50);
ImGui::TableSetupColumn("Subsheet", ImGuiTableColumnFlags_NoHide); ImGui::TableSetupColumn("Rows", ImGuiTableColumnFlags_WidthFixed, 50);
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 25); ImGui::TableHeadersRow();
ImGui::TableSetupColumn("Columns", ImGuiTableColumnFlags_WidthFixed, 50); drawSubsheetSelector(m_view.img().subsheet, path);
ImGui::TableSetupColumn("Rows", ImGuiTableColumnFlags_WidthFixed, 50); ImGui::EndTable();
ImGui::TableHeadersRow(); }
drawSubsheetSelector(m_view.img().subsheet, path);
ImGui::EndTable();
}
} }
ImGui::EndChild(); ImGui::EndChild();
} }
@@ -465,12 +462,8 @@ void TileSheetEditorImGui::drawPaletteMenu() noexcept {
} }
} }
// header // header
auto constexpr palTblFlags =
ImGuiTableFlags_RowBg |
ImGuiTableFlags_SizingStretchProp |
ImGuiTableFlags_ScrollY;
if (ImGui::BeginTable( if (ImGui::BeginTable(
"PaletteTable", 4, palTblFlags)) { "PaletteTable", 4, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingStretchProp)) {
ImGui::TableSetupColumn("Idx", 0, 0.6f); ImGui::TableSetupColumn("Idx", 0, 0.6f);
ImGui::TableSetupColumn("", 0, 0.22f); ImGui::TableSetupColumn("", 0, 0.22f);
ImGui::TableSetupColumn("Name", 0, 3); ImGui::TableSetupColumn("Name", 0, 3);
@@ -551,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)) {
ig::InputText("Name", m_name); ImGui::InputText("Name", m_name.data(), m_name.cap());
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,6 +26,11 @@
namespace nostalgia::core { namespace nostalgia::core {
Palette const TileSheetEditorModel::s_defaultPalette = {
.colorNames = {ox::Vector<ox::String>{{}}},
.pages = {{"Page 1", ox::Vector<Color16>(128)}},
};
// delete pixels of all non-leaf nodes // delete pixels of all non-leaf nodes
static void normalizeSubsheets(TileSheet::SubSheet &ss) noexcept { static void normalizeSubsheets(TileSheet::SubSheet &ss) noexcept {
if (ss.subsheets.empty()) { if (ss.subsheets.empty()) {
@@ -37,14 +42,7 @@ static void normalizeSubsheets(TileSheet::SubSheet &ss) noexcept {
} }
} }
TileSheetEditorModel::TileSheetEditorModel(studio::StudioContext &sctx, ox::StringView path, studio::UndoStack &undoStack):
Palette const TileSheetEditorModel::s_defaultPalette = {
.colorNames = {ox::Vector<ox::String>{{}}},
.pages = {{"Page 1", ox::Vector<Color16>(128)}},
};
TileSheetEditorModel::TileSheetEditorModel(
studio::StudioContext &sctx, ox::StringViewCR path, studio::UndoStack &undoStack):
m_sctx(sctx), m_sctx(sctx),
m_tctx(m_sctx.tctx), m_tctx(m_sctx.tctx),
m_path(path), m_path(path),
@@ -64,7 +62,7 @@ void TileSheetEditorModel::cut() {
TileSheetClipboard blankCb; TileSheetClipboard blankCb;
auto cb = ox::make_unique<TileSheetClipboard>(); auto cb = ox::make_unique<TileSheetClipboard>();
auto const&s = activeSubSheet(); auto const&s = activeSubSheet();
iterateSelectionRows(*m_selection, [&](int const x, int const y) { iterateSelectionRows(*m_selection, [&](int x, int y) {
auto pt = ox::Point{x, y}; auto pt = ox::Point{x, y};
auto const idx = core::idx(s, pt); auto const idx = core::idx(s, pt);
auto const c = getPixel(s, m_img.bpp, idx); auto const c = getPixel(s, m_img.bpp, idx);
@@ -75,8 +73,7 @@ void TileSheetEditorModel::cut() {
auto const pt1 = m_selection->a; auto const pt1 = m_selection->a;
auto const pt2 = ox::Point{s.columns * TileWidth, s.rows * TileHeight}; auto const pt2 = ox::Point{s.columns * TileWidth, s.rows * TileHeight};
turbine::setClipboardObject(m_tctx, std::move(cb)); turbine::setClipboardObject(m_tctx, std::move(cb));
pushCommand(ox::make<CutPasteCommand>( pushCommand(ox::make<CutPasteCommand>(CommandId::Cut, m_img, m_activeSubsSheetIdx, pt1, pt2, blankCb));
CommandId::Cut, m_img, m_activeSubsSheetIdx, pt1, pt2, blankCb));
} }
void TileSheetEditorModel::copy() { void TileSheetEditorModel::copy() {
@@ -84,7 +81,7 @@ void TileSheetEditorModel::copy() {
return; return;
} }
auto cb = ox::make_unique<TileSheetClipboard>(); auto cb = ox::make_unique<TileSheetClipboard>();
iterateSelectionRows(*m_selection, [&](int const x, int const y) { iterateSelectionRows(*m_selection, [&](int x, int y) {
auto pt = ox::Point{x, y}; auto pt = ox::Point{x, y};
const auto&s = activeSubSheet(); const auto&s = activeSubSheet();
const auto idx = core::idx(s, pt); const auto idx = core::idx(s, pt);
@@ -108,8 +105,7 @@ void TileSheetEditorModel::paste() {
auto const&s = activeSubSheet(); auto const&s = activeSubSheet();
auto const pt1 = m_selection->a; auto const pt1 = m_selection->a;
auto const pt2 = ox::Point{s.columns * TileWidth, s.rows * TileHeight}; auto const pt2 = ox::Point{s.columns * TileWidth, s.rows * TileHeight};
pushCommand(ox::make<CutPasteCommand>( pushCommand(ox::make<CutPasteCommand>(CommandId::Paste, m_img, m_activeSubsSheetIdx, pt1, pt2, *cb));
CommandId::Paste, m_img, m_activeSubsSheetIdx, pt1, pt2, *cb));
} }
bool TileSheetEditorModel::acceptsClipboardPayload() const noexcept { bool TileSheetEditorModel::acceptsClipboardPayload() const noexcept {
@@ -124,8 +120,8 @@ ox::StringView TileSheetEditorModel::palPath() const noexcept {
} }
constexpr ox::StringView uuidPrefix = "uuid://"; constexpr ox::StringView uuidPrefix = "uuid://";
if (ox::beginsWith(path, uuidPrefix)) { if (ox::beginsWith(path, uuidPrefix)) {
auto const uuid = ox::StringView(&path[uuidPrefix.bytes()], path.bytes() - uuidPrefix.bytes()); auto uuid = ox::StringView(&path[uuidPrefix.bytes()], path.bytes() - uuidPrefix.bytes());
auto const out = keelCtx(m_tctx).uuidToPath.at(uuid); auto out = keelCtx(m_tctx).uuidToPath.at(uuid);
if (out.error) { if (out.error) {
return {}; return {};
} }
@@ -135,14 +131,13 @@ ox::StringView TileSheetEditorModel::palPath() const noexcept {
} }
} }
ox::Error TileSheetEditorModel::setPalette(ox::StringViewCR path) noexcept { ox::Error TileSheetEditorModel::setPalette(ox::StringView path) noexcept {
OX_REQUIRE(uuid, keelCtx(m_tctx).pathToUuid.at(path)); OX_REQUIRE(uuid, keelCtx(m_tctx).pathToUuid.at(path));
pushCommand(ox::make<PaletteChangeCommand>( pushCommand(ox::make<PaletteChangeCommand>(activeSubSheetIdx(), m_img, uuid->toString()));
activeSubSheetIdx(), m_img, uuid->toString()));
return {}; return {};
} }
void TileSheetEditorModel::setPalettePage(size_t const pg) noexcept { void TileSheetEditorModel::setPalettePage(size_t pg) noexcept {
m_palettePage = ox::clamp<size_t>(pg, 0, m_pal->pages.size() - 1); m_palettePage = ox::clamp<size_t>(pg, 0, m_pal->pages.size() - 1);
m_updated = true; m_updated = true;
} }
@@ -151,7 +146,7 @@ size_t TileSheetEditorModel::palettePage() const noexcept {
return m_palettePage; return m_palettePage;
} }
void TileSheetEditorModel::drawCommand(ox::Point const&pt, std::size_t const palIdx) noexcept { void TileSheetEditorModel::drawCommand(ox::Point const&pt, std::size_t palIdx) noexcept {
const auto &activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx); const auto &activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx);
if (pt.x >= activeSubSheet.columns * TileWidth || pt.y >= activeSubSheet.rows * TileHeight) { if (pt.x >= activeSubSheet.columns * TileWidth || pt.y >= activeSubSheet.rows * TileHeight) {
return; return;
@@ -160,8 +155,7 @@ void TileSheetEditorModel::drawCommand(ox::Point const&pt, std::size_t const pal
if (m_ongoingDrawCommand) { if (m_ongoingDrawCommand) {
m_updated = m_updated || m_ongoingDrawCommand->append(idx); m_updated = m_updated || m_ongoingDrawCommand->append(idx);
} else if (getPixel(activeSubSheet, m_img.bpp, idx) != palIdx) { } else if (getPixel(activeSubSheet, m_img.bpp, idx) != palIdx) {
pushCommand(ox::make<DrawCommand>( pushCommand(ox::make<DrawCommand>(m_img, m_activeSubsSheetIdx, idx, static_cast<int>(palIdx)));
m_img, m_activeSubsSheetIdx, idx, static_cast<int>(palIdx)));
} }
} }
@@ -177,20 +171,16 @@ void TileSheetEditorModel::rmSubsheet(TileSheet::SubSheetIdx const&idx) noexcept
pushCommand(ox::make<RmSubSheetCommand>(m_img, idx)); pushCommand(ox::make<RmSubSheetCommand>(m_img, idx));
} }
void TileSheetEditorModel::insertTiles( void TileSheetEditorModel::insertTiles(TileSheet::SubSheetIdx const&idx, std::size_t tileIdx, std::size_t tileCnt) noexcept {
TileSheet::SubSheetIdx const&idx, std::size_t const tileIdx, std::size_t const tileCnt) noexcept {
pushCommand(ox::make<InsertTilesCommand>(m_img, idx, tileIdx, tileCnt)); pushCommand(ox::make<InsertTilesCommand>(m_img, idx, tileIdx, tileCnt));
} }
void TileSheetEditorModel::deleteTiles( void TileSheetEditorModel::deleteTiles(TileSheet::SubSheetIdx const&idx, std::size_t tileIdx, std::size_t tileCnt) noexcept {
TileSheet::SubSheetIdx const&idx, std::size_t const tileIdx, std::size_t const tileCnt) noexcept {
pushCommand(ox::make<DeleteTilesCommand>(m_img, idx, tileIdx, tileCnt)); pushCommand(ox::make<DeleteTilesCommand>(m_img, idx, tileIdx, tileCnt));
} }
ox::Error TileSheetEditorModel::updateSubsheet( ox::Error TileSheetEditorModel::updateSubsheet(TileSheet::SubSheetIdx const&idx, ox::StringView const&name, int cols, int rows) noexcept {
TileSheet::SubSheetIdx const&idx, ox::StringViewCR name, int const cols, int const rows) noexcept { pushCommand(ox::make<UpdateSubSheetCommand>(m_img, idx, ox::String(name), cols, rows));
OX_REQUIRE(cmd, ox::makeCatch<UpdateSubSheetCommand>(m_img, idx, name, cols, rows));
pushCommand(cmd);
return {}; return {};
} }
@@ -199,7 +189,7 @@ void TileSheetEditorModel::setActiveSubsheet(TileSheet::SubSheetIdx const&idx) n
this->activeSubsheetChanged.emit(m_activeSubsSheetIdx); this->activeSubsheetChanged.emit(m_activeSubsSheetIdx);
} }
void TileSheetEditorModel::fill(ox::Point const&pt, int const palIdx) noexcept { void TileSheetEditorModel::fill(ox::Point const&pt, int palIdx) noexcept {
auto const&activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx); auto const&activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx);
// build idx list // build idx list
if (pt.x >= activeSubSheet.columns * TileWidth || pt.y >= activeSubSheet.rows * TileHeight) { if (pt.x >= activeSubSheet.columns * TileWidth || pt.y >= activeSubSheet.rows * TileHeight) {
@@ -207,10 +197,10 @@ void TileSheetEditorModel::fill(ox::Point const&pt, int const palIdx) noexcept {
} }
ox::Array<bool, PixelsPerTile> updateMap = {}; ox::Array<bool, PixelsPerTile> updateMap = {};
auto const oldColor = getPixel(activeSubSheet, m_img.bpp, pt); auto const oldColor = getPixel(activeSubSheet, m_img.bpp, pt);
getFillPixels(activeSubSheet, updateMap, pt, oldColor); getFillPixels(updateMap, pt, oldColor);
ox::Vector<std::size_t> idxList; ox::Vector<std::size_t> idxList;
auto i = core::idx(activeSubSheet, pt) / PixelsPerTile * PixelsPerTile; auto i = core::idx(activeSubSheet, pt) / PixelsPerTile * PixelsPerTile;
for (auto const u : updateMap) { for (auto u : updateMap) {
if (u) { if (u) {
idxList.emplace_back(i); idxList.emplace_back(i);
} }
@@ -240,7 +230,7 @@ void TileSheetEditorModel::completeSelection() noexcept {
m_selTracker.finishSelection(); m_selTracker.finishSelection();
m_selection.emplace(m_selTracker.selection()); m_selection.emplace(m_selTracker.selection());
auto&pt = m_selection->b; auto&pt = m_selection->b;
auto const&s = activeSubSheet(); auto&s = activeSubSheet();
pt.x = ox::min(s.columns * TileWidth - 1, pt.x); pt.x = ox::min(s.columns * TileWidth - 1, pt.x);
pt.y = ox::min(s.rows * TileHeight - 1, pt.y); pt.y = ox::min(s.rows * TileHeight - 1, pt.y);
} }
@@ -285,44 +275,47 @@ ox::Error TileSheetEditorModel::saveFile() noexcept {
return m_sctx.project->writeObj(m_path, m_img, ox::ClawFormat::Metal); return m_sctx.project->writeObj(m_path, m_img, ox::ClawFormat::Metal);
} }
bool TileSheetEditorModel::pixelSelected(std::size_t const idx) const noexcept { bool TileSheetEditorModel::pixelSelected(std::size_t idx) const noexcept {
auto const&s = activeSubSheet(); auto const&s = activeSubSheet();
auto const pt = idxToPt(static_cast<int>(idx), s.columns); auto const pt = idxToPt(static_cast<int>(idx), s.columns);
return m_selection && m_selection->contains(pt); return m_selection && m_selection->contains(pt);
} }
void TileSheetEditorModel::getFillPixels( void TileSheetEditorModel::getFillPixels(ox::Span<bool> pixels, ox::Point const&pt, int oldColor) const noexcept {
TileSheet::SubSheet const&activeSubSheet, const auto &activeSubSheet = this->activeSubSheet();
ox::Span<bool> pixels, const auto tileIdx = [activeSubSheet](const ox::Point &pt) noexcept {
ox::Point const&pt, return ptToIdx(pt, activeSubSheet.columns) / PixelsPerTile;
int const oldColor) const noexcept { };
auto const idx = ptToIdx(pt, activeSubSheet.columns); // get points
auto const relIdx = idx % PixelsPerTile; const auto leftPt = pt + ox::Point(-1, 0);
if (pixels[relIdx] || getPixel(activeSubSheet, m_img.bpp, idx) != oldColor) { const auto rightPt = pt + ox::Point(1, 0);
return; const auto topPt = pt + ox::Point(0, -1);
} const auto bottomPt = pt + ox::Point(0, 1);
// calculate indices
const auto idx = ptToIdx(pt, activeSubSheet.columns);
const auto leftIdx = ptToIdx(leftPt, activeSubSheet.columns);
const auto rightIdx = ptToIdx(rightPt, activeSubSheet.columns);
const auto topIdx = ptToIdx(topPt, activeSubSheet.columns);
const auto bottomIdx = ptToIdx(bottomPt, activeSubSheet.columns);
const auto tile = tileIdx(pt);
// mark pixels to update // mark pixels to update
pixels[relIdx] = true; pixels[idx % PixelsPerTile] = true;
if (pt.x % TileWidth != 0) { if (!pixels[leftIdx % PixelsPerTile] && tile == tileIdx(leftPt) && getPixel(activeSubSheet, m_img.bpp, leftIdx) == oldColor) {
auto const leftPt = pt + ox::Point{-1, 0}; getFillPixels(pixels, leftPt, oldColor);
getFillPixels(activeSubSheet, pixels, leftPt, oldColor);
} }
if (pt.x % TileWidth != TileWidth - 1) { if (!pixels[rightIdx % PixelsPerTile] && tile == tileIdx(rightPt) && getPixel(activeSubSheet, m_img.bpp, rightIdx) == oldColor) {
auto const rightPt = pt + ox::Point{1, 0}; getFillPixels(pixels, rightPt, oldColor);
getFillPixels(activeSubSheet, pixels, rightPt, oldColor);
} }
if (pt.y % TileHeight != 0) { if (!pixels[topIdx % PixelsPerTile] && tile == tileIdx(topPt) && getPixel(activeSubSheet, m_img.bpp, topIdx) == oldColor) {
auto const topPt = pt + ox::Point{0, -1}; getFillPixels(pixels, topPt, oldColor);
getFillPixels(activeSubSheet, pixels, topPt, oldColor);
} }
if (pt.y % TileHeight != TileHeight - 1) { if (!pixels[bottomIdx % PixelsPerTile] && tile == tileIdx(bottomPt) && getPixel(activeSubSheet, m_img.bpp, bottomIdx) == oldColor) {
auto const bottomPt = pt + ox::Point{0, 1}; getFillPixels(pixels, bottomPt, oldColor);
getFillPixels(activeSubSheet, pixels, bottomPt, oldColor);
} }
} }
void TileSheetEditorModel::pushCommand(studio::UndoCommand *cmd) noexcept { void TileSheetEditorModel::pushCommand(studio::UndoCommand *cmd) noexcept {
std::ignore = m_undoStack.push(ox::UPtr<studio::UndoCommand>{cmd}); std::ignore = m_undoStack.push(ox::UPtr<studio::UndoCommand>(cmd));
m_ongoingDrawCommand = dynamic_cast<DrawCommand*>(cmd); m_ongoingDrawCommand = dynamic_cast<DrawCommand*>(cmd);
m_updated = true; m_updated = true;
} }

View File

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

View File

@@ -11,7 +11,7 @@ target_link_libraries(
target_compile_definitions( target_compile_definitions(
NostalgiaStudio PUBLIC NostalgiaStudio PUBLIC
OLYMPIC_APP_VERSION="d2024.12.4" OLYMPIC_APP_VERSION="d2024.12.1"
) )
install( install(

View File

@@ -18,7 +18,7 @@
<string>APPL</string> <string>APPL</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>d2024.12.4</string> <string>0.0.0</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
<string>12.0.0</string> <string>12.0.0</string>
@@ -30,6 +30,6 @@
<string>True</string> <string>True</string>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>
<string>Copyright (c) 2016-2025 Gary Talent &lt;gary@drinkingtea.net&gt;</string> <string>Copyright (c) 2016-2023 Gary Talent &lt;gary@drinkingtea.net&gt;</string>
</dict> </dict>
</plist> </plist>

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] {
ig::InputText("Name", m_projectName); ImGui::InputText("Name", m_projectName.data(), m_projectName.cap());
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));