Squashed 'deps/nostalgia/' changes from eed115b2..7d9f363b

7d9f363b [nostalgia/core/studio] Show color names in TileSheetEditor
ababc2a7 [nostalgia/core/studio] Add Name to colors table in PaletteEditor
bfd4bc3c [nostalgia/core] Revise Palette system, give TileSheetEditor a config file
8826d51e [studio] Add configio.hpp to studio.hpp
c021e5e7 [ox/oc] Fix OC not dealing with certain int types properly
7d8a8e0e [keel] Cleanup
95a69b72 [ox/std] Fix String::c_str to always retrun a valid C str
e4c38660 [nostalgia/core] Fix subsheet resize not to read garbage
67cf3ae8 [ox/std] Ensure ox::String always has at least a null terminator
2761f23d [nostalgia/developer-handbook] Update serialization notes
6c170d31 [nostalgia/developer-handbook] Update serialization notes
d20bfc77 [nostalgia/developer-handbook] Update serialization notes
07ecbde1 [nostalgia/developer-handbook] Update notes on error handling
fbe2fcd3 [nostalgia/developer-handbook] Update notes on error handling
a8bb99b6 [turbine] Remove ClipboardObject::typeMatches
667dd21a [turbine] Make ClipboardObject::typId return a StringView
5d89370c [turbine] Rework getClipboardObject to use ox::ModelTypeId_v
b968ec8a [keel] Remove redundant move
ec45ffb7 [studio] Fix build error
97dc0274 [nostalgia/core/studio] Add acceptsClipboardPayload to TileSheetEditor
a138f60f [studio] Add acceptsClipboardPayload to Editor
791d1950 [turbine] Make ClipboardObject use ox::ModelTypeId_v for typeId
78eb8fca [keel] Cleanup pack logging
0b8051b6 [ox/preloader] Fix alignment issue
5a426829 [nostalgia/core/studio] Cleanup TileSheet selection, fix copy/paste bug
9d2fe0e8 [studio] Add size function to Selection
f1894699 [keel] Remove setAsset
27b38ed2 [keel,studio] Fix hotloading for files that get loaded as multiple types
2bb7c514 [studio/modlib] Fix type desc writing logic inversion
5177cfb0 [studio/modlib] Make Project::mkdir only mkdir if dir does not exist
f9a14433 [studio/modlib] Add variant of ComboBox that takes callback
e62426b0 [keel] Ensure consistent asset IDs in AssetManager
af634bd4 [ox/fs] Add FileSystem::exists
49b859ec [studio/modlib] Give Selection constructors
19a41201 [studio/modlib] Make iterateSelection return errors properly
f69b8afa [nostalgia] Remove use of deleted function
9c98b5e2 [studio/modlib] Remove color.hpp
1f87216d [nostalgia/core] Add applySelectionColor
94c59604 [nostalgia/core/opengl] Fix for Ox changes
8ee016c1 [studio/modlib] Add SelectionTracker
dc20c667 [ox/std] Add conversion functions for geo types, cleanup
407e5424 [ox/std] Remove SmallMap dtor, replace timing code with steady_clock
3b188696 [ox/claw] Remove enum type from ClawFormat
0fab6c7c [ox/preloader] Remove debug code
a72b865d [studio/modlib] Add function for exporting selection color
c0479604 [studio,nostalgia/studio] Make executing UndoCommands report errors
a1c89906 [nostalgia/studio] Make UndoCommand undo/redo return ox::Error
7fb0549c [nostalgia/core] Revert some auto formatting done by CLion...
37e65ab0 [nostalgia/core/studio] Fix Subsheet width to update properly
9105b1ec [ox/std] Fix Linux build
fbeb0815 [ox/model] Fix type params in buildTypeId
b882a47e [ox/std] Fix resize to set null terminator
660f2f56 [ox/std] Rework FileReader into StreamReader
aa83c2a6 [nostalgia/core/studio] Remove some unnecessary copying
4a2b1fd7 [studio,keel] Make fileChanged emit UUID as well as path, add uuidUrlToUuid
08f958fb [ox/std] Add IntegerRange_c
a651d45a [ox/std] Fix Vector insert functions
9e9f317c [studio] Make UndoCommand::mergeWith take a reference
f5a02ce9 [nostalgia/core/gba] Fix build
6971c310 [studio] Add NoChangeException
c47f48eb [keel] Add/cleanup UUID/path lookup functions
76771e7b [nostalgia/core] Add tileColumns and tileRows functions
f6a0ae20 [ox/std] Fix some Windows warnings
752c8c1d [glutils] Fix type conversion that happened on Windows
af3bff1a [glutils] Add FrameBuffer::sizef
87416e13 [ox/std] Make MallocaPtr call destructor
047b4396 [ox/std] Make Point and Size members int32_t
40b8da4d [studio/modlib] Cleanup
123c4125 [ox/std] Add SmallMap::pairs(), SmallMap model
963ec5d3 [ox/std] Add operator-> to SpanIterator
6df77a23 [glutils] Add size function to FrameBuffer
df412cf8 [ox/std] Add missing typenames
ae30ef36 Merge commit 'b66cef7127e97269fc6072a6f66ccc08990f6d2e'
095a1135 Merge commit 'f48824793cfce315971fe2e699ece198c7a79407'
ce1836ab Merge commit '1e041bd2ebfe5ace7bed3906faf60345aa98a8bc'
7d1641fa Merge commit '420fa96463f59c4a4a7cd66b16b0ad01ab0d55e6'
423212b2 [studio] Add missing include
60da1063 Merge commit 'bd416f82e25f1b710ab2b7890274571dd3fcd53d'
60d1996f [keel] Minor optimization

git-subtree-dir: deps/nostalgia
git-subtree-split: 7d9f363bfa7a2c64f5c4bcfd0b6686f3f5678119
This commit is contained in:
2024-05-30 23:14:19 -05:00
parent b66cef7127
commit 76b8b03a2c
90 changed files with 1633 additions and 832 deletions

View File

@@ -2,6 +2,7 @@
* Copyright 2016 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include <algorithm>
#include <filesystem>
#include <imgui.h>
@@ -196,10 +197,10 @@ void StudioUI::drawMenu() noexcept {
if (ImGui::BeginMenu("Edit")) {
auto undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr;
if (ImGui::MenuItem("Undo", "Ctrl+Z", false, undoStack && undoStack->canUndo())) {
undoStack->undo();
oxLogError(undoStack->undo());
}
if (ImGui::MenuItem("Redo", "Ctrl+Y", false, undoStack && undoStack->canRedo())) {
undoStack->redo();
oxLogError(undoStack->redo());
}
ImGui::Separator();
if (ImGui::MenuItem("Copy", "Ctrl+C", false, m_activeEditor && m_activeEditor->copyEnabled())) {
@@ -208,7 +209,7 @@ void StudioUI::drawMenu() noexcept {
if (ImGui::MenuItem("Cut", "Ctrl+X", false, m_activeEditor && m_activeEditor->cutEnabled())) {
m_activeEditor->cut();
}
if (ImGui::MenuItem("Paste", "Ctrl+V", false, m_activeEditor && m_activeEditor->pasteEnabled()) && m_activeEditor) {
if (ImGui::MenuItem("Paste", "Ctrl+V", false, m_activeEditor && m_activeEditor->pasteEnabled())) {
m_activeEditor->paste();
}
ImGui::EndMenu();
@@ -314,14 +315,14 @@ void StudioUI::toggleProjectExplorer() noexcept {
void StudioUI::redo() noexcept {
auto undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr;
if (undoStack && undoStack->canRedo()) {
m_activeEditor->undoStack()->redo();
oxLogError(m_activeEditor->undoStack()->redo());
}
}
void StudioUI::undo() noexcept {
auto undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr;
if (undoStack && undoStack->canUndo()) {
m_activeEditor->undoStack()->undo();
oxLogError(m_activeEditor->undoStack()->undo());
}
}

View File

@@ -22,4 +22,9 @@ struct StudioContext {
ui(pUi), tctx(pTctx) {}
};
[[nodiscard]]
inline keel::Context &keelCtx(StudioContext &ctx) noexcept {
return keelCtx(ctx.tctx);
}
}

View File

@@ -43,6 +43,9 @@ class BaseEditor: public Widget {
virtual void paste();
[[nodiscard]]
virtual bool acceptsClipboardPayload() const noexcept;
virtual void exportFile();
virtual void keyStateChanged(turbine::Key key, bool down);
@@ -131,14 +134,14 @@ class Editor: public studio::BaseEditor {
[[nodiscard]]
UndoStack *undoStack() noexcept final;
void pushCommand(ox::UPtr<UndoCommand> &&cmd) noexcept;
ox::Error pushCommand(ox::UPtr<UndoCommand> &&cmd) noexcept;
template<typename UC, typename ...Args>
void pushCommand(Args&&... args) noexcept {
ox::Error pushCommand(Args&&... args) noexcept {
try {
m_undoStack.push(ox::make_unique<UC>(ox::forward<Args>(args)...));
return m_undoStack.push(ox::make_unique<UC>(ox::forward<Args>(args)...));
} catch (ox::Exception const&ex) {
oxLogError(ex.toError());
return ex.toError();
}
}

View File

@@ -34,7 +34,7 @@ ox::Result<T> getDragDropPayload(ox::CStringView name) noexcept {
static_cast<size_t>(payload->DataSize)});
}
ox::Error setDragDropPayload(ox::CStringView name, auto const &obj) noexcept {
ox::Error setDragDropPayload(ox::CStringView name, auto const&obj) noexcept {
oxRequire(buff, ox::writeClaw(obj, ox::ClawFormat::Metal));
ImGui::SetDragDropPayload(name.c_str(), buff.data(), buff.size());
return {};
@@ -148,6 +148,19 @@ bool BeginPopup(turbine::Context &ctx, ox::CStringView popupName, bool &show, Im
*/
bool ComboBox(ox::CStringView lbl, ox::SpanView<ox::String> list, size_t &selectedIdx) noexcept;
/**
*
* @param lbl
* @param callback
* @param selectedIdx
* @return true if new value selected, false otherwise
*/
bool ComboBox(
ox::CStringView lbl,
std::function<ox::CStringView(size_t)> const&f,
size_t strCnt,
size_t &selectedIdx) noexcept;
bool FileComboBox(
ox::CStringView lbl,
studio::StudioContext &sctx,

View File

@@ -31,7 +31,7 @@ enum class ProjectEvent {
constexpr ox::Result<ox::StringView> fileExt(ox::CRStringView path) noexcept {
auto const extStart = ox::find(path.crbegin(), path.crend(), '.').offset();
if (!extStart) {
return OxError(1, "Cannot open a file without valid extension.");
return OxError(1, "file path does not have valid extension");
}
return substr(path, extStart + 1);
}
@@ -47,6 +47,7 @@ constexpr ox::StringView parentDir(ox::StringView path) noexcept {
class Project {
private:
ox::SmallMap<ox::String, ox::Optional<ox::ClawFormat>> m_typeFmt;
keel::Context &m_ctx;
ox::String m_path;
ox::String m_projectDataDir;
@@ -75,7 +76,15 @@ class Project {
ox::Error writeObj(
ox::CRStringView path,
T const&obj,
ox::ClawFormat fmt = ox::ClawFormat::Metal) noexcept;
ox::ClawFormat fmt) noexcept;
/**
* Writes a MetalClaw object to the project at the given path.
*/
template<typename T>
ox::Error writeObj(
ox::CRStringView path,
T const&obj) noexcept;
template<typename T>
ox::Result<T> loadObj(ox::CRStringView path) const noexcept;
@@ -115,7 +124,7 @@ class Project {
// file.
ox::Signal<ox::Error(ox::CRStringView)> fileRecognized;
ox::Signal<ox::Error(ox::CRStringView)> fileDeleted;
ox::Signal<ox::Error(ox::CRStringView)> fileUpdated;
ox::Signal<ox::Error(ox::StringView, ox::UUID)> fileUpdated;
};
@@ -130,15 +139,24 @@ ox::Error Project::writeObj(ox::CRStringView path, T const&obj, ox::ClawFormat f
oxReturnError(ox::buildTypeDef(&m_typeStore, &obj));
}
oxRequire(desc, m_typeStore.get<T>());
auto const descExists = m_fs.stat(ox::sfmt("{}/{}", m_typeDescPath, buildTypeId(*desc))).error != 0;
auto const descPath = ox::sfmt("{}/{}", m_typeDescPath, buildTypeId(*desc));
auto const descExists = m_fs.exists(descPath);
if (!descExists) {
oxReturnError(writeTypeStore());
}
oxReturnError(keel::setAsset(m_ctx, path, obj));
fileUpdated.emit(path);
oxReturnError(keel::reloadAsset(m_ctx, path));
oxRequire(uuid, pathToUuid(m_ctx, path));
fileUpdated.emit(path, uuid);
return {};
}
template<typename T>
ox::Error Project::writeObj(ox::CRStringView path, T const&obj) noexcept {
oxRequire(ext, fileExt(path));
auto const fmt = m_typeFmt[ext].or_value(ox::ClawFormat::Metal);
return writeObj(path, obj, fmt);
}
template<typename T>
ox::Result<T> Project::loadObj(ox::CRStringView path) const noexcept {
oxRequire(buff, loadBuff(path));

View File

@@ -0,0 +1,133 @@
#pragma once
#include <imgui.h>
#include <ox/std/math.hpp>
#include <ox/std/point.hpp>
#include <ox/std/size.hpp>
#include <ox/std/typetraits.hpp>
namespace studio {
struct Selection {
ox::Point a, b;
constexpr Selection() noexcept = default;
constexpr Selection(ox::Point const&pA, ox::Point const&pB) noexcept: a(pA), b(pB) {}
[[nodiscard]]
constexpr ox::Size size() const noexcept {
return {b.x - a.x, b.y - a.y};
}
[[nodiscard]]
constexpr bool contains(ox::Point const&pt) const noexcept {
return a.x <= pt.x && a.y <= pt.y
&& b.x >= pt.x && b.y >= pt.y;
}
};
constexpr auto iterateSelection(studio::Selection const&sel, auto const&cb) {
constexpr auto retErr = ox::is_same_v<decltype(cb(0, 0)), ox::Error>;
for (auto x = sel.a.x; x <= sel.b.x; ++x) {
for (auto y = sel.a.y; y <= sel.b.y; ++y) {
if constexpr(retErr) {
oxReturnError(cb(x, y));
} else {
cb(x, y);
}
}
}
if constexpr(retErr) {
return ox::Error{};
}
};
constexpr auto iterateSelectionRows(studio::Selection const&sel, auto const&cb) {
constexpr auto retErr = ox::is_same_v<decltype(cb(0, 0)), ox::Error>;
for (auto y = sel.a.y; y <= sel.b.y; ++y) {
for (auto x = sel.a.x; x <= sel.b.x; ++x) {
if constexpr(retErr) {
oxReturnError(cb(x, y));
} else {
cb(x, y);
}
}
}
if constexpr(retErr) {
return ox::Error{};
}
};
class SelectionTracker {
private:
bool m_selectionOngoing{};
ox::Point m_pointA;
ox::Point m_pointB;
public:
[[nodiscard]]
constexpr bool selectionOngoing() const noexcept {
return m_selectionOngoing;
}
constexpr void startSelection(ox::Point cursor) noexcept {
m_pointA = cursor;
m_pointB = cursor;
m_selectionOngoing = true;
}
/**
*
* @param cursor
* @param allowStart
* @return true if changed, false otherwise
*/
constexpr bool updateCursorPoint(ox::Point cursor, bool allowStart = true) noexcept {
auto changed = false;
if (!m_selectionOngoing && allowStart) {
m_pointA = cursor;
m_selectionOngoing = true;
}
if (m_selectionOngoing) {
m_pointB = cursor;
changed = true;
}
return changed;
}
constexpr void updateCursorPoint(ox::Vec2 cursor, bool allowStart = true) noexcept {
updateCursorPoint(
ox::Point{
static_cast<int32_t>(cursor.x),
static_cast<int32_t>(cursor.y),
},
allowStart);
}
constexpr void updateCursorPoint(ImVec2 cursor, bool allowStart = true) noexcept {
updateCursorPoint(
ox::Point{
static_cast<int32_t>(cursor.x),
static_cast<int32_t>(cursor.y),
},
allowStart);
}
constexpr void finishSelection() noexcept {
m_selectionOngoing = {};
}
[[nodiscard]]
constexpr Selection selection() const noexcept {
return {
{
ox::min(m_pointA.x, m_pointB.x),
ox::min(m_pointA.y, m_pointB.y),
},
{
ox::max(m_pointA.x, m_pointB.x),
ox::max(m_pointA.y, m_pointB.y),
},
};
}
};
}

View File

@@ -4,6 +4,7 @@
#pragma once
#include <studio/configio.hpp>
#include <studio/context.hpp>
#include <studio/editor.hpp>
#include <studio/filedialog.hpp>
@@ -12,6 +13,7 @@
#include <studio/itemmaker.hpp>
#include <studio/popup.hpp>
#include <studio/project.hpp>
#include <studio/selectiontracker.hpp>
#include <studio/task.hpp>
#include <studio/undocommand.hpp>
#include <studio/undostack.hpp>

View File

@@ -4,16 +4,26 @@
#pragma once
#include <source_location>
#include <ox/std/error.hpp>
namespace studio {
class NoChangesException: public ox::Exception {
public:
inline NoChangesException(std::source_location sloc = std::source_location::current()):
ox::Exception(sloc.file_name(), sloc.line(), 1, "Command makes no changes.") {}
};
class UndoCommand {
public:
virtual ~UndoCommand() noexcept = default;
virtual void redo() noexcept = 0;
virtual void undo() noexcept = 0;
virtual ox::Error redo() noexcept = 0;
virtual ox::Error undo() noexcept = 0;
[[nodiscard]]
virtual int commandId() const noexcept = 0;
virtual bool mergeWith(UndoCommand const*cmd) noexcept;
virtual bool mergeWith(UndoCommand const&cmd) noexcept;
};
}

View File

@@ -19,11 +19,11 @@ class UndoStack {
std::size_t m_stackIdx = 0;
public:
void push(ox::UPtr<UndoCommand> &&cmd) noexcept;
ox::Error push(ox::UPtr<UndoCommand> &&cmd) noexcept;
void redo() noexcept;
ox::Error redo() noexcept;
void undo() noexcept;
ox::Error undo() noexcept;
[[nodiscard]]
constexpr bool canRedo() const noexcept {

View File

@@ -23,6 +23,10 @@ void BaseEditor::copy() {
void BaseEditor::paste() {
}
bool BaseEditor::acceptsClipboardPayload() const noexcept {
return {};
}
void BaseEditor::exportFile() {
}
@@ -95,7 +99,7 @@ void BaseEditor::setPasteEnabled(bool v) {
}
bool BaseEditor::pasteEnabled() const noexcept {
return m_pasteEnabled;
return m_pasteEnabled && acceptsClipboardPayload();
}
ox::Error BaseEditor::saveItem() noexcept {
@@ -127,8 +131,8 @@ ox::CStringView Editor::itemDisplayName() const noexcept {
return m_itemName;
}
void Editor::pushCommand(ox::UPtr<UndoCommand> &&cmd) noexcept {
m_undoStack.push(std::move(cmd));
ox::Error Editor::pushCommand(ox::UPtr<UndoCommand> &&cmd) noexcept {
return m_undoStack.push(std::move(cmd));
}
UndoStack *Editor::undoStack() noexcept {

View File

@@ -100,6 +100,26 @@ bool ComboBox(
return out;
}
bool ComboBox(
ox::CStringView lbl,
std::function<ox::CStringView(size_t)> const&f,
size_t strCnt,
size_t &selectedIdx) noexcept {
bool out{};
auto const first = selectedIdx < strCnt ? f(selectedIdx).c_str() : "";
if (ImGui::BeginCombo(lbl.c_str(), first, 0)) {
for (auto i = 0u; i < strCnt; ++i) {
const auto selected = (selectedIdx == i);
if (ImGui::Selectable(f(i).c_str(), selected) && selectedIdx != i) {
selectedIdx = i;
out = true;
}
}
ImGui::EndCombo();
}
return out;
}
bool FileComboBox(
ox::CStringView lbl,
studio::StudioContext &sctx,

View File

@@ -56,9 +56,13 @@ ox::FileSystem &Project::romFs() noexcept {
}
ox::Error Project::mkdir(ox::CRStringView path) const noexcept {
oxReturnError(m_fs.mkdir(path, true));
fileUpdated.emit(path);
return {};
auto const [stat, err] = m_fs.stat(path);
if (err) {
oxReturnError(m_fs.mkdir(path, true));
fileUpdated.emit(path, {});
}
return stat.fileType == ox::FileType::Directory ?
ox::Error{} : OxError(1, "path exists as normal file");
}
ox::Result<ox::FileStat> Project::stat(ox::CRStringView path) const noexcept {
@@ -115,9 +119,9 @@ ox::Error Project::writeBuff(ox::CRStringView path, ox::Buffer const&buff) noexc
ox::Buffer outBuff;
outBuff.reserve(buff.size() + HdrSz);
ox::BufferWriter writer(&outBuff);
auto const [uuid, err] = m_ctx.pathToUuid.at(path);
auto const [uuid, err] = pathToUuid(m_ctx, path);
if (!err) {
oxReturnError(keel::writeUuidHeader(writer, *uuid));
oxReturnError(keel::writeUuidHeader(writer, uuid));
}
oxReturnError(writer.write(buff.data(), buff.size()));
auto const newFile = m_fs.stat(path).error != 0;
@@ -126,7 +130,7 @@ ox::Error Project::writeBuff(ox::CRStringView path, ox::Buffer const&buff) noexc
fileAdded.emit(path);
indexFile(path);
} else {
fileUpdated.emit(path);
fileUpdated.emit(path, uuid);
}
return {};
}

View File

@@ -3,7 +3,7 @@
namespace studio {
bool UndoCommand::mergeWith(UndoCommand const*) noexcept {
bool UndoCommand::mergeWith(UndoCommand const&) noexcept {
return false;
}

View File

@@ -6,35 +6,39 @@
namespace studio {
void 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();) {
std::ignore = m_stack.erase(i);
}
cmd->redo();
oxReturnError(cmd->redo());
redoTriggered.emit(cmd.get());
changeTriggered.emit(cmd.get());
if (m_stack.empty() || !(*m_stack.back().value)->mergeWith(cmd.get())) {
if (m_stack.empty() || !(*m_stack.back().value)->mergeWith(*cmd)) {
m_stack.emplace_back(std::move(cmd));
++m_stackIdx;
}
return {};
}
void UndoStack::redo() noexcept {
ox::Error UndoStack::redo() noexcept {
if (m_stackIdx < m_stack.size()) {
auto &c = m_stack[m_stackIdx++];
c->redo();
auto &c = m_stack[m_stackIdx];
oxReturnError(c->redo());
++m_stackIdx;
redoTriggered.emit(c.get());
changeTriggered.emit(c.get());
}
return {};
}
void UndoStack::undo() noexcept {
ox::Error UndoStack::undo() noexcept {
if (m_stackIdx) {
auto &c = m_stack[--m_stackIdx];
c->undo();
oxReturnError(c->undo());
undoTriggered.emit(c.get());
changeTriggered.emit(c.get());
}
return {};
}
}