Compare commits
No commits in common. "master" and "release-d2025.02" have entirely different histories.
master
...
release-d2
@ -4,7 +4,7 @@ on: [push]
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: olympic
|
runs-on: nostalgia
|
||||||
steps:
|
steps:
|
||||||
- name: Check out repository code
|
- name: Check out repository code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
2
deps/buildcore/base.mk
vendored
2
deps/buildcore/base.mk
vendored
@ -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} ${BC_CMD_PY3} -m mypy ${BC_VAR_SCRIPTS}
|
${BC_CMD_ENVRUN} 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
|
||||||
|
45
deps/ox/src/ox/fs/filesystem/filesystem.cpp
vendored
45
deps/ox/src/ox/fs/filesystem/filesystem.cpp
vendored
@ -37,30 +37,6 @@ Error FileSystem::read(const FileAddress &addr, void *buffer, std::size_t size)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<Buffer> FileSystem::read(FileAddress const &addr, size_t const size) noexcept {
|
|
||||||
Result<Buffer> out;
|
|
||||||
out.value.resize(size);
|
|
||||||
switch (addr.type()) {
|
|
||||||
case FileAddressType::Inode:
|
|
||||||
OX_RETURN_ERROR(readFileInode(addr.getInode().value, out.value.data(), size));
|
|
||||||
break;
|
|
||||||
case FileAddressType::ConstPath:
|
|
||||||
case FileAddressType::Path:
|
|
||||||
OX_RETURN_ERROR(readFilePath(StringView{addr.getPath().value}, out.value.data(), size));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return ox::Error{1};
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<Buffer> FileSystem::read(StringViewCR path, size_t const size) noexcept {
|
|
||||||
Result<Buffer> out;
|
|
||||||
out.value.resize(size);
|
|
||||||
OX_RETURN_ERROR(readFilePath(path, out.value.data(), size));
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<Buffer> FileSystem::read(const FileAddress &addr) noexcept {
|
Result<Buffer> FileSystem::read(const FileAddress &addr) noexcept {
|
||||||
OX_REQUIRE(s, stat(addr));
|
OX_REQUIRE(s, stat(addr));
|
||||||
Buffer buff(static_cast<std::size_t>(s.size));
|
Buffer buff(static_cast<std::size_t>(s.size));
|
||||||
@ -75,33 +51,18 @@ Result<Buffer> FileSystem::read(StringViewCR path) noexcept {
|
|||||||
return buff;
|
return buff;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error FileSystem::read(
|
Error FileSystem::read(const FileAddress &addr, std::size_t readStart, std::size_t readSize, void *buffer, std::size_t *size) noexcept {
|
||||||
FileAddress const &addr,
|
|
||||||
std::size_t const readStart,
|
|
||||||
std::size_t const readSize,
|
|
||||||
void *buffer,
|
|
||||||
std::size_t *size) noexcept {
|
|
||||||
switch (addr.type()) {
|
switch (addr.type()) {
|
||||||
case FileAddressType::Inode:
|
case FileAddressType::Inode:
|
||||||
return readFileInodeRange(addr.getInode().value, readStart, readSize, buffer, size);
|
return read(addr.getInode().value, readStart, readSize, buffer, size);
|
||||||
case FileAddressType::ConstPath:
|
case FileAddressType::ConstPath:
|
||||||
case FileAddressType::Path:
|
case FileAddressType::Path:
|
||||||
return readFilePathRange(addr.getPath().value, readStart, readSize, buffer, size);
|
return ox::Error(2, "Unsupported for path lookups");
|
||||||
default:
|
default:
|
||||||
return ox::Error(1);
|
return ox::Error(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<size_t> FileSystem::read(
|
|
||||||
StringViewCR path,
|
|
||||||
std::size_t const readStart,
|
|
||||||
std::size_t const readSize,
|
|
||||||
Span<char> buff) noexcept {
|
|
||||||
size_t szOut{buff.size()};
|
|
||||||
OX_RETURN_ERROR(readFilePathRange(path, readStart, readSize, buff.data(), &szOut));
|
|
||||||
return szOut;
|
|
||||||
}
|
|
||||||
|
|
||||||
Error FileSystem::write(const FileAddress &addr, const void *buffer, uint64_t size, FileType fileType) noexcept {
|
Error FileSystem::write(const FileAddress &addr, const void *buffer, uint64_t size, FileType fileType) noexcept {
|
||||||
switch (addr.type()) {
|
switch (addr.type()) {
|
||||||
case FileAddressType::Inode:
|
case FileAddressType::Inode:
|
||||||
|
38
deps/ox/src/ox/fs/filesystem/filesystem.hpp
vendored
38
deps/ox/src/ox/fs/filesystem/filesystem.hpp
vendored
@ -41,10 +41,6 @@ class FileSystem {
|
|||||||
|
|
||||||
Error read(const FileAddress &addr, void *buffer, std::size_t size) noexcept;
|
Error read(const FileAddress &addr, void *buffer, std::size_t size) noexcept;
|
||||||
|
|
||||||
Result<Buffer> read(FileAddress const &addr, size_t size) noexcept;
|
|
||||||
|
|
||||||
Result<Buffer> read(StringViewCR path, size_t size) noexcept;
|
|
||||||
|
|
||||||
Result<Buffer> read(const FileAddress &addr) noexcept;
|
Result<Buffer> read(const FileAddress &addr) noexcept;
|
||||||
|
|
||||||
Result<Buffer> read(StringViewCR path) noexcept;
|
Result<Buffer> read(StringViewCR path) noexcept;
|
||||||
@ -57,24 +53,7 @@ class FileSystem {
|
|||||||
return readFileInode(inode, buffer, buffSize);
|
return readFileInode(inode, buffer, buffSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
Error read(
|
Error read(const FileAddress &addr, std::size_t readStart, std::size_t readSize, void *buffer, std::size_t *size) noexcept;
|
||||||
FileAddress const &addr,
|
|
||||||
size_t readStart,
|
|
||||||
size_t readSize,
|
|
||||||
void *buffer,
|
|
||||||
size_t *size) noexcept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param path
|
|
||||||
* @param readStart
|
|
||||||
* @param readSize
|
|
||||||
* @param buffer
|
|
||||||
* @param size
|
|
||||||
* @return error or number of bytes read
|
|
||||||
*/
|
|
||||||
Result<size_t> read(
|
|
||||||
StringViewCR path, size_t readStart, size_t readSize, ox::Span<char> buff) noexcept;
|
|
||||||
|
|
||||||
virtual Result<Vector<String>> ls(StringViewCR dir) const noexcept = 0;
|
virtual Result<Vector<String>> ls(StringViewCR dir) const noexcept = 0;
|
||||||
|
|
||||||
@ -161,10 +140,7 @@ class FileSystem {
|
|||||||
|
|
||||||
virtual Error readFileInode(uint64_t inode, void *buffer, std::size_t size) noexcept = 0;
|
virtual Error readFileInode(uint64_t inode, void *buffer, std::size_t size) noexcept = 0;
|
||||||
|
|
||||||
virtual Error readFilePathRange(
|
virtual Error readFileInodeRange(uint64_t inode, std::size_t readStart, std::size_t readSize, void *buffer, std::size_t *size) noexcept = 0;
|
||||||
StringViewCR path, size_t readStart, size_t readSize, void *buffer, size_t *buffSize) noexcept = 0;
|
|
||||||
|
|
||||||
virtual Error readFileInodeRange(uint64_t inode, size_t readStart, size_t readSize, void *buffer, size_t *size) noexcept = 0;
|
|
||||||
|
|
||||||
virtual Error removePath(StringViewCR path, bool recursive) noexcept = 0;
|
virtual Error removePath(StringViewCR path, bool recursive) noexcept = 0;
|
||||||
|
|
||||||
@ -235,9 +211,6 @@ class FileSystemTemplate: public MemFS {
|
|||||||
|
|
||||||
Error readFileInodeRange(uint64_t inode, std::size_t readStart, std::size_t readSize, void *buffer, std::size_t *size) noexcept override;
|
Error readFileInodeRange(uint64_t inode, std::size_t readStart, std::size_t readSize, void *buffer, std::size_t *size) noexcept override;
|
||||||
|
|
||||||
Error readFilePathRange(
|
|
||||||
StringViewCR path, size_t readStart, size_t readSize, void *buffer, size_t *buffSize) noexcept override;
|
|
||||||
|
|
||||||
Error removePath(StringViewCR path, bool recursive) noexcept override;
|
Error removePath(StringViewCR path, bool recursive) noexcept override;
|
||||||
|
|
||||||
Result<const char*> directAccessInode(uint64_t) const noexcept override;
|
Result<const char*> directAccessInode(uint64_t) const noexcept override;
|
||||||
@ -385,13 +358,6 @@ Error FileSystemTemplate<FileStore, Directory>::readFileInodeRange(uint64_t inod
|
|||||||
return m_fs.read(inode, readStart, readSize, reinterpret_cast<uint8_t*>(buffer), size);
|
return m_fs.read(inode, readStart, readSize, reinterpret_cast<uint8_t*>(buffer), size);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FileStore, typename Directory>
|
|
||||||
Error FileSystemTemplate<FileStore, Directory>::readFilePathRange(
|
|
||||||
StringViewCR path, size_t readStart, size_t readSize, void *buffer, size_t *buffSize) noexcept {
|
|
||||||
OX_REQUIRE(s, stat(path));
|
|
||||||
return readFileInodeRange(s.inode, readStart, readSize, buffer, buffSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename FileStore, typename Directory>
|
template<typename FileStore, typename Directory>
|
||||||
Error FileSystemTemplate<FileStore, Directory>::removePath(StringViewCR path, bool recursive) noexcept {
|
Error FileSystemTemplate<FileStore, Directory>::removePath(StringViewCR path, bool recursive) noexcept {
|
||||||
OX_REQUIRE(fd, fileSystemData());
|
OX_REQUIRE(fd, fileSystemData());
|
||||||
|
19
deps/ox/src/ox/fs/filesystem/passthroughfs.cpp
vendored
19
deps/ox/src/ox/fs/filesystem/passthroughfs.cpp
vendored
@ -154,25 +154,6 @@ Error PassThroughFS::readFileInode(uint64_t, void*, std::size_t) noexcept {
|
|||||||
return ox::Error(1, "readFileInode(uint64_t, void*, std::size_t) is not supported by PassThroughFS");
|
return ox::Error(1, "readFileInode(uint64_t, void*, std::size_t) is not supported by PassThroughFS");
|
||||||
}
|
}
|
||||||
|
|
||||||
Error PassThroughFS::readFilePathRange(
|
|
||||||
StringViewCR path, size_t const readStart, size_t readSize, void *buffer, size_t *buffSize) noexcept {
|
|
||||||
try {
|
|
||||||
std::ifstream file(m_path / stripSlash(path), std::ios::binary | std::ios::ate);
|
|
||||||
auto const size = static_cast<size_t>(file.tellg());
|
|
||||||
readSize = ox::min(readSize, size);
|
|
||||||
file.seekg(static_cast<off_t>(readStart), std::ios::beg);
|
|
||||||
if (readSize > *buffSize) {
|
|
||||||
oxTracef("ox.fs.PassThroughFS.read.error", "Read failed: Buffer too small: {}", path);
|
|
||||||
return ox::Error{1};
|
|
||||||
}
|
|
||||||
file.read(static_cast<char*>(buffer), static_cast<std::streamsize>(readSize));
|
|
||||||
return {};
|
|
||||||
} catch (std::fstream::failure const &f) {
|
|
||||||
oxTracef("ox.fs.PassThroughFS.read.error", "Read of {} failed: {}", path, f.what());
|
|
||||||
return ox::Error{2};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Error PassThroughFS::readFileInodeRange(uint64_t, std::size_t, std::size_t, void*, std::size_t*) noexcept {
|
Error PassThroughFS::readFileInodeRange(uint64_t, std::size_t, std::size_t, void*, std::size_t*) noexcept {
|
||||||
// unsupported
|
// unsupported
|
||||||
return ox::Error(1, "read(uint64_t, std::size_t, std::size_t, void*, std::size_t*) is not supported by PassThroughFS");
|
return ox::Error(1, "read(uint64_t, std::size_t, std::size_t, void*, std::size_t*) is not supported by PassThroughFS");
|
||||||
|
@ -71,9 +71,6 @@ class PassThroughFS: public FileSystem {
|
|||||||
|
|
||||||
Error readFileInode(uint64_t inode, void *buffer, std::size_t size) noexcept override;
|
Error readFileInode(uint64_t inode, void *buffer, std::size_t size) noexcept override;
|
||||||
|
|
||||||
Error readFilePathRange(
|
|
||||||
StringViewCR path, size_t readStart, size_t readSize, void *buffer, size_t *buffSize) noexcept override;
|
|
||||||
|
|
||||||
Error readFileInodeRange(uint64_t inode, std::size_t readStart, std::size_t readSize, void *buffer, std::size_t *size) noexcept override;
|
Error readFileInodeRange(uint64_t inode, std::size_t readStart, std::size_t readSize, void *buffer, std::size_t *size) noexcept override;
|
||||||
|
|
||||||
Error removePath(StringViewCR path, bool recursive) noexcept override;
|
Error removePath(StringViewCR path, bool recursive) noexcept override;
|
||||||
|
@ -5,9 +5,7 @@
|
|||||||
* 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 functionality to TileSheet Editor.
|
* Add Flip X and Flip Y button for 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 create directories.
|
* Add ability to create directories.
|
||||||
@ -17,5 +15,3 @@
|
|||||||
* 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
|
|
||||||
|
@ -9,6 +9,5 @@ target_sources(
|
|||||||
inserttilescommand.cpp
|
inserttilescommand.cpp
|
||||||
palettechangecommand.cpp
|
palettechangecommand.cpp
|
||||||
rmsubsheetcommand.cpp
|
rmsubsheetcommand.cpp
|
||||||
rotatecommand.cpp
|
|
||||||
updatesubsheetcommand.cpp
|
updatesubsheetcommand.cpp
|
||||||
)
|
)
|
||||||
|
@ -17,7 +17,6 @@ enum class CommandId {
|
|||||||
DeleteTile,
|
DeleteTile,
|
||||||
FlipX,
|
FlipX,
|
||||||
FlipY,
|
FlipY,
|
||||||
Rotate,
|
|
||||||
InsertTile,
|
InsertTile,
|
||||||
MoveSubSheet,
|
MoveSubSheet,
|
||||||
UpdateSubSheet,
|
UpdateSubSheet,
|
||||||
|
@ -2,68 +2,15 @@
|
|||||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
#include "drawcommand.hpp"
|
#include "drawcommand.hpp"
|
||||||
|
|
||||||
namespace nostalgia::gfx {
|
namespace nostalgia::gfx {
|
||||||
|
|
||||||
constexpr void iterateLine(ox::Point const &a, ox::Point const &b, auto const &f) noexcept {
|
|
||||||
auto const rise = b.y - a.y;
|
|
||||||
auto const run = b.x - a.x;
|
|
||||||
auto const slope = static_cast<double>(rise) / static_cast<double>(run);
|
|
||||||
auto y = static_cast<double>(a.y);
|
|
||||||
auto const w = abs(run);
|
|
||||||
if (rise > 0) {
|
|
||||||
if (run > 0) {
|
|
||||||
auto constexpr xmod = 1;
|
|
||||||
for (int32_t i = 0; i < w; ++i) {
|
|
||||||
auto const climb = static_cast<int32_t>(ceil(y + slope - y));
|
|
||||||
f(a.x + i * xmod, static_cast<int32_t>(y), climb * xmod);
|
|
||||||
y += slope * xmod;
|
|
||||||
}
|
|
||||||
} else if (run < 0) {
|
|
||||||
auto constexpr xmod = -1;
|
|
||||||
for (int32_t i = 0; i < w; ++i) {
|
|
||||||
auto const climb = static_cast<int32_t>(floor(y + slope - y));
|
|
||||||
f(a.x + i * xmod, static_cast<int32_t>(y), climb * xmod);
|
|
||||||
y += slope * xmod;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
f(a.x, a.y, rise);
|
|
||||||
}
|
|
||||||
} else if (rise < 0) {
|
|
||||||
if (run > 0) {
|
|
||||||
auto constexpr xmod = 1;
|
|
||||||
for (int32_t i = 0; i < w; ++i) {
|
|
||||||
auto const climb = static_cast<int32_t>(floor(y + slope - y));
|
|
||||||
f(a.x + i * xmod, static_cast<int32_t>(y), climb * xmod);
|
|
||||||
y += slope * xmod;
|
|
||||||
}
|
|
||||||
} else if (run < 0) {
|
|
||||||
auto constexpr xmod = -1;
|
|
||||||
for (int32_t i = 0; i < w; ++i) {
|
|
||||||
auto const climb = static_cast<int32_t>(ceil(y + slope - y));
|
|
||||||
f(a.x + i * xmod, static_cast<int32_t>(y), climb * xmod);
|
|
||||||
y += slope * xmod;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
f(a.x, a.y, rise - 1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
auto const xmod = run > 0 ? 1 : -1;
|
|
||||||
for (int32_t i = 0; i < w; ++i) {
|
|
||||||
f(a.x + i * xmod, static_cast<int32_t>(y), 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
DrawCommand::DrawCommand(
|
DrawCommand::DrawCommand(
|
||||||
TileSheet &img,
|
TileSheet &img,
|
||||||
TileSheet::SubSheetIdx subSheetIdx,
|
TileSheet::SubSheetIdx subSheetIdx,
|
||||||
std::size_t idx,
|
std::size_t idx,
|
||||||
int const palIdx) noexcept:
|
int palIdx) noexcept:
|
||||||
m_img(img),
|
m_img(img),
|
||||||
m_subSheetIdx(std::move(subSheetIdx)),
|
m_subSheetIdx(std::move(subSheetIdx)),
|
||||||
m_palIdx(palIdx) {
|
m_palIdx(palIdx) {
|
||||||
@ -74,8 +21,8 @@ DrawCommand::DrawCommand(
|
|||||||
DrawCommand::DrawCommand(
|
DrawCommand::DrawCommand(
|
||||||
TileSheet &img,
|
TileSheet &img,
|
||||||
TileSheet::SubSheetIdx subSheetIdx,
|
TileSheet::SubSheetIdx subSheetIdx,
|
||||||
ox::SpanView<std::size_t> const&idxList,
|
ox::Vector<std::size_t> const&idxList,
|
||||||
int const palIdx) noexcept:
|
int palIdx) noexcept:
|
||||||
m_img(img),
|
m_img(img),
|
||||||
m_subSheetIdx(std::move(subSheetIdx)),
|
m_subSheetIdx(std::move(subSheetIdx)),
|
||||||
m_palIdx(palIdx) {
|
m_palIdx(palIdx) {
|
||||||
@ -85,12 +32,11 @@ DrawCommand::DrawCommand(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DrawCommand::append(std::size_t const idx) noexcept {
|
bool DrawCommand::append(std::size_t idx) noexcept {
|
||||||
auto &subsheet = getSubSheet(m_img, m_subSheetIdx);
|
auto &subsheet = getSubSheet(m_img, m_subSheetIdx);
|
||||||
if (m_changes.back().value->idx != idx && getPixel(subsheet, idx) != m_palIdx) {
|
if (m_changes.back().value->idx != idx && getPixel(subsheet, idx) != m_palIdx) {
|
||||||
// duplicate entries are bad
|
// duplicate entries are bad
|
||||||
auto existing = find_if(
|
auto existing = ox::find_if(m_changes.cbegin(), m_changes.cend(), [idx](auto const&c) {
|
||||||
m_changes.cbegin(), m_changes.cend(), [idx](auto const&c) {
|
|
||||||
return c.idx == idx;
|
return c.idx == idx;
|
||||||
});
|
});
|
||||||
if (existing == m_changes.cend()) {
|
if (existing == m_changes.cend()) {
|
||||||
@ -102,7 +48,7 @@ bool DrawCommand::append(std::size_t const idx) noexcept {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DrawCommand::append(ox::SpanView<std::size_t> const&idxList) noexcept {
|
bool DrawCommand::append(ox::Vector<std::size_t> const&idxList) noexcept {
|
||||||
auto out = false;
|
auto out = false;
|
||||||
for (auto idx : idxList) {
|
for (auto idx : idxList) {
|
||||||
out = append(idx) || out;
|
out = append(idx) || out;
|
||||||
@ -110,28 +56,6 @@ bool DrawCommand::append(ox::SpanView<std::size_t> const&idxList) noexcept {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawCommand::lineUpdate(ox::Point a, ox::Point b) noexcept {
|
|
||||||
std::ignore = undo();
|
|
||||||
m_changes.clear();
|
|
||||||
auto &ss = getSubSheet(m_img, m_subSheetIdx);
|
|
||||||
if (a.x < b.x) { ++b.x; }
|
|
||||||
if (a.y < b.y) { ++b.y; }
|
|
||||||
if (a.x > b.x) { --b.x; }
|
|
||||||
iterateLine(a, b, [this, &ss](int32_t const x, int32_t const y, int32_t const h) {
|
|
||||||
int const mod = h < 0 ? -1 : 1;
|
|
||||||
auto const range = h * mod;
|
|
||||||
for (int32_t i{}; i < range; ++i) {
|
|
||||||
auto const idx = ptToIdx(x, y + i * mod, ss.columns * TileWidth);
|
|
||||||
if (idx < ss.pixels.size()) {
|
|
||||||
if (m_palIdx != getPixel(ss, idx)) {
|
|
||||||
m_changes.emplace_back(static_cast<uint32_t>(idx), getPixel(ss, idx));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
std::ignore = redo();
|
|
||||||
}
|
|
||||||
|
|
||||||
ox::Error DrawCommand::redo() noexcept {
|
ox::Error DrawCommand::redo() noexcept {
|
||||||
auto &subsheet = getSubSheet(m_img, m_subSheetIdx);
|
auto &subsheet = getSubSheet(m_img, m_subSheetIdx);
|
||||||
for (auto const&c : m_changes) {
|
for (auto const&c : m_changes) {
|
||||||
@ -156,8 +80,4 @@ TileSheet::SubSheetIdx const&DrawCommand::subsheetIdx() const noexcept {
|
|||||||
return m_subSheetIdx;
|
return m_subSheetIdx;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawCommand::finish() noexcept {
|
|
||||||
setObsolete(m_changes.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -33,14 +33,12 @@ class DrawCommand: public TileSheetCommand {
|
|||||||
DrawCommand(
|
DrawCommand(
|
||||||
TileSheet &img,
|
TileSheet &img,
|
||||||
TileSheet::SubSheetIdx subSheetIdx,
|
TileSheet::SubSheetIdx subSheetIdx,
|
||||||
ox::SpanView<std::size_t> const&idxList,
|
ox::Vector<std::size_t> const&idxList,
|
||||||
int palIdx) noexcept;
|
int palIdx) noexcept;
|
||||||
|
|
||||||
bool append(std::size_t idx) noexcept;
|
bool append(std::size_t idx) noexcept;
|
||||||
|
|
||||||
bool append(ox::SpanView<std::size_t> const&idxList) noexcept;
|
bool append(ox::Vector<std::size_t> const&idxList) noexcept;
|
||||||
|
|
||||||
void lineUpdate(ox::Point a, ox::Point b) noexcept;
|
|
||||||
|
|
||||||
ox::Error redo() noexcept final;
|
ox::Error redo() noexcept final;
|
||||||
|
|
||||||
@ -52,8 +50,6 @@ class DrawCommand: public TileSheetCommand {
|
|||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
TileSheet::SubSheetIdx const&subsheetIdx() const noexcept override;
|
TileSheet::SubSheetIdx const&subsheetIdx() const noexcept override;
|
||||||
|
|
||||||
void finish() noexcept;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
namespace nostalgia::gfx {
|
namespace nostalgia::gfx {
|
||||||
|
|
||||||
RmSubSheetCommand::RmSubSheetCommand(TileSheet &img, TileSheet::SubSheetIdx idx) noexcept:
|
gfx::RmSubSheetCommand::RmSubSheetCommand(TileSheet &img, TileSheet::SubSheetIdx idx) noexcept:
|
||||||
m_img(img),
|
m_img(img),
|
||||||
m_idx(std::move(idx)),
|
m_idx(std::move(idx)),
|
||||||
m_parentIdx(m_idx) {
|
m_parentIdx(m_idx) {
|
||||||
|
@ -1,126 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "rotatecommand.hpp"
|
|
||||||
|
|
||||||
namespace nostalgia::gfx {
|
|
||||||
|
|
||||||
static void rotateLeft(
|
|
||||||
TileSheet::SubSheet &ss,
|
|
||||||
ox::Point const &pt,
|
|
||||||
ox::Point const &pt1,
|
|
||||||
ox::Point const &pt2,
|
|
||||||
int const depth = 0) noexcept {
|
|
||||||
if (depth >= 4) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto const dstPt = ox::Point{pt1.x + pt.y, pt2.y - pt.x};
|
|
||||||
auto const srcIdx = ptToIdx(pt + pt1, ss.columns);
|
|
||||||
auto const dstIdx = ptToIdx(dstPt, ss.columns);
|
|
||||||
auto const src = ss.pixels[srcIdx];
|
|
||||||
auto &dst = ss.pixels[dstIdx];
|
|
||||||
rotateLeft(ss, dstPt - pt1, pt1, pt2, depth + 1);
|
|
||||||
dst = src;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rotateLeft(TileSheet::SubSheet &ss, ox::Point const &pt1, ox::Point const &pt2) noexcept {
|
|
||||||
auto const w = pt2.x - pt1.x;
|
|
||||||
auto const h = pt2.y - pt1.y;
|
|
||||||
for (int x = 0; x <= w / 2; ++x) {
|
|
||||||
for (int y = 0; y <= h / 2; ++y) {
|
|
||||||
rotateLeft(ss, {x, y}, pt1, pt2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rotateRight(
|
|
||||||
TileSheet::SubSheet &ss,
|
|
||||||
ox::Point const &pt,
|
|
||||||
ox::Point const &pt1,
|
|
||||||
ox::Point const &pt2,
|
|
||||||
int const depth = 0) noexcept {
|
|
||||||
if (depth >= 4) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto const dstPt = ox::Point{pt2.x - pt.y, pt1.y + pt.x};
|
|
||||||
auto const srcIdx = ptToIdx(pt + pt1, ss.columns);
|
|
||||||
auto const dstIdx = ptToIdx(dstPt, ss.columns);
|
|
||||||
auto const src = ss.pixels[srcIdx];
|
|
||||||
auto &dst = ss.pixels[dstIdx];
|
|
||||||
rotateRight(ss, dstPt - pt1, pt1, pt2, depth + 1);
|
|
||||||
dst = src;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rotateRight(TileSheet::SubSheet &ss, ox::Point const &pt1, ox::Point const &pt2) noexcept {
|
|
||||||
auto const w = pt2.x - pt1.x;
|
|
||||||
auto const h = pt2.y - pt1.y;
|
|
||||||
for (int x = 0; x <= w / 2; ++x) {
|
|
||||||
for (int y = 0; y <= h / 2; ++y) {
|
|
||||||
rotateRight(ss, {x, y}, pt1, pt2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
RotateCommand::RotateCommand(
|
|
||||||
TileSheet &img,
|
|
||||||
TileSheet::SubSheetIdx idx,
|
|
||||||
Direction const dir) noexcept:
|
|
||||||
m_img(img),
|
|
||||||
m_idx(std::move(idx)),
|
|
||||||
m_pt2{[this] {
|
|
||||||
auto &ss = getSubSheet(m_img, m_idx);
|
|
||||||
return ox::Point{ss.columns * TileWidth - 1, ss.rows * TileHeight - 1};
|
|
||||||
}()},
|
|
||||||
m_dir{dir} {
|
|
||||||
}
|
|
||||||
|
|
||||||
RotateCommand::RotateCommand(
|
|
||||||
TileSheet &img,
|
|
||||||
TileSheet::SubSheetIdx idx,
|
|
||||||
ox::Point const &pt1,
|
|
||||||
ox::Point const &pt2,
|
|
||||||
Direction const dir) noexcept:
|
|
||||||
m_img(img),
|
|
||||||
m_idx(std::move(idx)),
|
|
||||||
m_pt1{pt1},
|
|
||||||
m_pt2{pt2},
|
|
||||||
m_dir{dir} {
|
|
||||||
}
|
|
||||||
|
|
||||||
ox::Error RotateCommand::redo() noexcept {
|
|
||||||
auto &ss = getSubSheet(m_img, m_idx);
|
|
||||||
switch (m_dir) {
|
|
||||||
case Direction::Left:
|
|
||||||
rotateLeft(ss, m_pt1, m_pt2);
|
|
||||||
break;
|
|
||||||
case Direction::Right:
|
|
||||||
rotateRight(ss, m_pt1, m_pt2);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
ox::Error RotateCommand::undo() noexcept {
|
|
||||||
auto &ss = getSubSheet(m_img, m_idx);
|
|
||||||
switch (m_dir) {
|
|
||||||
case Direction::Left:
|
|
||||||
rotateRight(ss, m_pt1, m_pt2);
|
|
||||||
break;
|
|
||||||
case Direction::Right:
|
|
||||||
rotateLeft(ss, m_pt1, m_pt2);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
int RotateCommand::commandId() const noexcept {
|
|
||||||
return static_cast<int>(CommandId::Rotate);
|
|
||||||
}
|
|
||||||
|
|
||||||
TileSheet::SubSheetIdx const&RotateCommand::subsheetIdx() const noexcept {
|
|
||||||
return m_idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "commands.hpp"
|
|
||||||
|
|
||||||
namespace nostalgia::gfx {
|
|
||||||
|
|
||||||
class RotateCommand: public TileSheetCommand {
|
|
||||||
public:
|
|
||||||
enum class Direction {
|
|
||||||
Right,
|
|
||||||
Left,
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
TileSheet &m_img;
|
|
||||||
TileSheet::SubSheetIdx m_idx;
|
|
||||||
ox::Point const m_pt1;
|
|
||||||
ox::Point const m_pt2;
|
|
||||||
Direction const m_dir;
|
|
||||||
|
|
||||||
public:
|
|
||||||
RotateCommand(TileSheet &img, TileSheet::SubSheetIdx idx, Direction dir) noexcept;
|
|
||||||
|
|
||||||
RotateCommand(
|
|
||||||
TileSheet &img,
|
|
||||||
TileSheet::SubSheetIdx idx,
|
|
||||||
ox::Point const &pt1,
|
|
||||||
ox::Point const &pt2,
|
|
||||||
Direction dir) noexcept;
|
|
||||||
|
|
||||||
ox::Error redo() noexcept final;
|
|
||||||
|
|
||||||
ox::Error undo() noexcept final;
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
int commandId() const noexcept final;
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
TileSheet::SubSheetIdx const&subsheetIdx() const noexcept override;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@ -152,12 +152,6 @@ void TileSheetEditorImGui::keyStateChanged(turbine::Key const key, bool const do
|
|||||||
setCutEnabled(false);
|
setCutEnabled(false);
|
||||||
setPasteEnabled(false);
|
setPasteEnabled(false);
|
||||||
m_model.clearSelection();
|
m_model.clearSelection();
|
||||||
} else if (key == turbine::Key::Alpha_E) {
|
|
||||||
m_tool = TileSheetTool::Line;
|
|
||||||
setCopyEnabled(false);
|
|
||||||
setCutEnabled(false);
|
|
||||||
setPasteEnabled(false);
|
|
||||||
m_model.clearSelection();
|
|
||||||
} else if (key == turbine::Key::Alpha_S) {
|
} else if (key == turbine::Key::Alpha_S) {
|
||||||
m_tool = TileSheetTool::Select;
|
m_tool = TileSheetTool::Select;
|
||||||
setCopyEnabled(true);
|
setCopyEnabled(true);
|
||||||
@ -220,7 +214,7 @@ void TileSheetEditorImGui::draw(studio::StudioContext&) noexcept {
|
|||||||
ImGui::BeginChild("Controls", {s_palViewWidth - 8, paneSize.y}, true);
|
ImGui::BeginChild("Controls", {s_palViewWidth - 8, paneSize.y}, true);
|
||||||
{
|
{
|
||||||
auto const controlsSize = ImGui::GetContentRegionAvail();
|
auto const controlsSize = ImGui::GetContentRegionAvail();
|
||||||
ImGui::BeginChild("ToolBox", {0, 32}, true);
|
ImGui::BeginChild("ToolBox", {168, 32}, true);
|
||||||
{
|
{
|
||||||
auto const btnSz = ImVec2{45, 14};
|
auto const btnSz = ImVec2{45, 14};
|
||||||
if (ImGui::Selectable("Select", m_tool == TileSheetTool::Select, 0, btnSz)) {
|
if (ImGui::Selectable("Select", m_tool == TileSheetTool::Select, 0, btnSz)) {
|
||||||
@ -236,34 +230,18 @@ void TileSheetEditorImGui::draw(studio::StudioContext&) noexcept {
|
|||||||
m_tool = TileSheetTool::Fill;
|
m_tool = TileSheetTool::Fill;
|
||||||
m_model.clearSelection();
|
m_model.clearSelection();
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
|
||||||
if (ImGui::Selectable("Line", m_tool == TileSheetTool::Line, 0, btnSz)) {
|
|
||||||
m_tool = TileSheetTool::Line;
|
|
||||||
m_model.clearSelection();
|
|
||||||
}
|
|
||||||
//ImGui::SameLine();
|
|
||||||
//size_t i{};
|
|
||||||
//ig::ComboBox("##Operations", ox::Array<ox::CStringView, 1>{"Operations"}, i);
|
|
||||||
}
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
ImGui::BeginChild("OperationsBox", {0, 35}, ImGuiWindowFlags_NoTitleBar);
|
ImGui::SameLine();
|
||||||
|
ImGui::BeginChild("OperationsBox", {0, 32}, true);
|
||||||
{
|
{
|
||||||
if (ImGui::BeginCombo("##Operations", "Operations", 0)) {
|
auto constexpr btnSz = ImVec2{55, 16};
|
||||||
if (ImGui::Selectable("Flip X", false)) {
|
if (ig::PushButton("Flip X", btnSz)) {
|
||||||
oxLogError(m_model.flipX());
|
oxLogError(m_model.flipX());
|
||||||
}
|
}
|
||||||
if (ImGui::Selectable("Flip Y", false)) {
|
ImGui::SameLine();
|
||||||
oxLogError(m_model.flipY());
|
if (ig::PushButton("Flip Y", btnSz)) {
|
||||||
}
|
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();
|
||||||
@ -470,9 +448,6 @@ void TileSheetEditorImGui::drawTileSheet(ox::Vec2 const&fbSize) noexcept {
|
|||||||
case TileSheetTool::Fill:
|
case TileSheetTool::Fill:
|
||||||
m_view.clickFill(fbSize, clickPos(winPos, mousePos));
|
m_view.clickFill(fbSize, clickPos(winPos, mousePos));
|
||||||
break;
|
break;
|
||||||
case TileSheetTool::Line:
|
|
||||||
m_view.clickLine(fbSize, clickPos(winPos, mousePos));
|
|
||||||
break;
|
|
||||||
case TileSheetTool::Select:
|
case TileSheetTool::Select:
|
||||||
m_view.clickSelect(fbSize, clickPos(winPos, mousePos));
|
m_view.clickSelect(fbSize, clickPos(winPos, mousePos));
|
||||||
break;
|
break;
|
||||||
|
@ -24,7 +24,6 @@
|
|||||||
#include "tilesheeteditormodel.hpp"
|
#include "tilesheeteditormodel.hpp"
|
||||||
|
|
||||||
#include "commands/movesubsheetcommand.hpp"
|
#include "commands/movesubsheetcommand.hpp"
|
||||||
#include "commands/rotatecommand.hpp"
|
|
||||||
|
|
||||||
namespace nostalgia::gfx {
|
namespace nostalgia::gfx {
|
||||||
|
|
||||||
@ -141,15 +140,11 @@ 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 const palIdx) noexcept {
|
||||||
if (m_lastDrawUpdatePt == pt) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto const &activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx);
|
auto const &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;
|
||||||
}
|
}
|
||||||
m_lastDrawUpdatePt = pt;
|
|
||||||
auto const idx = gfx::idx(activeSubSheet, pt);
|
auto const idx = gfx::idx(activeSubSheet, pt);
|
||||||
if (m_ongoingDrawCommand) {
|
if (m_ongoingDrawCommand) {
|
||||||
m_updated = m_updated || m_ongoingDrawCommand->append(idx);
|
m_updated = m_updated || m_ongoingDrawCommand->append(idx);
|
||||||
@ -159,31 +154,8 @@ void TileSheetEditorModel::drawCommand(ox::Point const &pt, std::size_t const pa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TileSheetEditorModel::drawLineCommand(ox::Point const &pt, std::size_t const palIdx) noexcept {
|
|
||||||
if (m_lastDrawUpdatePt == pt) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto const &activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx);
|
|
||||||
if (pt.x >= activeSubSheet.columns * TileWidth || pt.y >= activeSubSheet.rows * TileHeight) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_lastDrawUpdatePt = pt;
|
|
||||||
auto const idx = gfx::idx(activeSubSheet, pt);
|
|
||||||
if (m_ongoingDrawCommand) {
|
|
||||||
m_ongoingDrawCommand->lineUpdate(m_lineStartPt, pt);
|
|
||||||
m_updated = true;
|
|
||||||
} else {
|
|
||||||
std::ignore = pushCommand(ox::make<DrawCommand>(
|
|
||||||
m_img, m_activeSubsSheetIdx, idx, static_cast<int>(palIdx)));
|
|
||||||
m_lineStartPt = pt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TileSheetEditorModel::endDrawCommand() noexcept {
|
void TileSheetEditorModel::endDrawCommand() noexcept {
|
||||||
if (m_ongoingDrawCommand) {
|
m_ongoingDrawCommand = nullptr;
|
||||||
m_ongoingDrawCommand->finish();
|
|
||||||
m_ongoingDrawCommand = nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TileSheetEditorModel::addSubsheet(TileSheet::SubSheetIdx const&parentIdx) noexcept {
|
void TileSheetEditorModel::addSubsheet(TileSheet::SubSheetIdx const&parentIdx) noexcept {
|
||||||
@ -241,28 +213,6 @@ void TileSheetEditorModel::fill(ox::Point const&pt, int const palIdx) noexcept {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ox::Error TileSheetEditorModel::rotateLeft() noexcept {
|
|
||||||
auto &ss = activeSubSheet();
|
|
||||||
ox::Point pt1, pt2{ss.columns * TileWidth - 1, ss.rows * TileHeight - 1};
|
|
||||||
if (m_selection) {
|
|
||||||
pt1 = m_selection->a;
|
|
||||||
pt2 = m_selection->b;
|
|
||||||
}
|
|
||||||
return pushCommand(ox::make<RotateCommand>(
|
|
||||||
m_img, m_activeSubsSheetIdx, pt1, pt2, RotateCommand::Direction::Left));
|
|
||||||
}
|
|
||||||
|
|
||||||
ox::Error TileSheetEditorModel::rotateRight() noexcept {
|
|
||||||
auto &ss = activeSubSheet();
|
|
||||||
ox::Point pt1, pt2{ss.columns * TileWidth - 1, ss.rows * TileHeight - 1};
|
|
||||||
if (m_selection) {
|
|
||||||
pt1 = m_selection->a;
|
|
||||||
pt2 = m_selection->b;
|
|
||||||
}
|
|
||||||
return pushCommand(ox::make<RotateCommand>(
|
|
||||||
m_img, m_activeSubsSheetIdx, pt1, pt2, RotateCommand::Direction::Right));
|
|
||||||
}
|
|
||||||
|
|
||||||
void TileSheetEditorModel::setSelection(studio::Selection const&sel) noexcept {
|
void TileSheetEditorModel::setSelection(studio::Selection const&sel) noexcept {
|
||||||
m_selection.emplace(sel);
|
m_selection.emplace(sel);
|
||||||
m_updated = true;
|
m_updated = true;
|
||||||
@ -303,7 +253,6 @@ ox::Error TileSheetEditorModel::markUpdatedCmdId(studio::UndoCommand const*cmd)
|
|||||||
m_pal = keel::AssetRef<Palette>{};
|
m_pal = keel::AssetRef<Palette>{};
|
||||||
}
|
}
|
||||||
m_palettePage = ox::min<size_t>(pal().pages.size(), 0);
|
m_palettePage = ox::min<size_t>(pal().pages.size(), 0);
|
||||||
setPalPath();
|
|
||||||
paletteChanged.emit();
|
paletteChanged.emit();
|
||||||
}
|
}
|
||||||
auto const tsCmd = dynamic_cast<TileSheetCommand const*>(cmd);
|
auto const tsCmd = dynamic_cast<TileSheetCommand const*>(cmd);
|
||||||
@ -355,16 +304,6 @@ 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)));
|
||||||
}
|
}
|
||||||
|
@ -34,8 +34,6 @@ class TileSheetEditorModel: public ox::SignalHandler {
|
|||||||
class DrawCommand *m_ongoingDrawCommand = nullptr;
|
class DrawCommand *m_ongoingDrawCommand = nullptr;
|
||||||
studio::SelectionTracker m_selTracker;
|
studio::SelectionTracker m_selTracker;
|
||||||
ox::Optional<studio::Selection> m_selection;
|
ox::Optional<studio::Selection> m_selection;
|
||||||
ox::Point m_lineStartPt;
|
|
||||||
ox::Point m_lastDrawUpdatePt;
|
|
||||||
bool m_updated = false;
|
bool m_updated = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -73,8 +71,6 @@ class TileSheetEditorModel: public ox::SignalHandler {
|
|||||||
|
|
||||||
void drawCommand(ox::Point const&pt, std::size_t palIdx) noexcept;
|
void drawCommand(ox::Point const&pt, std::size_t palIdx) noexcept;
|
||||||
|
|
||||||
void drawLineCommand(ox::Point const&pt, std::size_t palIdx) noexcept;
|
|
||||||
|
|
||||||
void endDrawCommand() noexcept;
|
void endDrawCommand() noexcept;
|
||||||
|
|
||||||
void addSubsheet(TileSheet::SubSheetIdx const&parentIdx) noexcept;
|
void addSubsheet(TileSheet::SubSheetIdx const&parentIdx) noexcept;
|
||||||
@ -106,10 +102,6 @@ class TileSheetEditorModel: public ox::SignalHandler {
|
|||||||
|
|
||||||
void fill(ox::Point const&pt, int palIdx) noexcept;
|
void fill(ox::Point const&pt, int palIdx) noexcept;
|
||||||
|
|
||||||
ox::Error rotateLeft() noexcept;
|
|
||||||
|
|
||||||
ox::Error rotateRight() noexcept;
|
|
||||||
|
|
||||||
void setSelection(studio::Selection const&sel) noexcept;
|
void setSelection(studio::Selection const&sel) noexcept;
|
||||||
|
|
||||||
void select(ox::Point const&pt) noexcept;
|
void select(ox::Point const&pt) noexcept;
|
||||||
@ -138,9 +130,6 @@ 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:
|
||||||
|
@ -11,8 +11,7 @@
|
|||||||
|
|
||||||
namespace nostalgia::gfx {
|
namespace nostalgia::gfx {
|
||||||
|
|
||||||
TileSheetEditorView::TileSheetEditorView(
|
TileSheetEditorView::TileSheetEditorView(studio::StudioContext &sctx, ox::StringView path, studio::UndoStack &undoStack):
|
||||||
studio::StudioContext &sctx, ox::StringViewCR path, studio::UndoStack &undoStack):
|
|
||||||
m_model(sctx, path, undoStack),
|
m_model(sctx, path, undoStack),
|
||||||
m_pixelsDrawer(m_model) {
|
m_pixelsDrawer(m_model) {
|
||||||
glBindVertexArray(0);
|
glBindVertexArray(0);
|
||||||
@ -77,11 +76,6 @@ void TileSheetEditorView::clickDraw(ox::Vec2 const&paneSize, ox::Vec2 const&clic
|
|||||||
m_model.drawCommand(pt, m_palIdx);
|
m_model.drawCommand(pt, m_palIdx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TileSheetEditorView::clickLine(ox::Vec2 const&paneSize, ox::Vec2 const&clickPos) noexcept {
|
|
||||||
auto const pt = clickPoint(paneSize, clickPos);
|
|
||||||
m_model.drawLineCommand(pt, m_palIdx);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TileSheetEditorView::clickSelect(ox::Vec2 const&paneSize, ox::Vec2 const&clickPos) noexcept {
|
void TileSheetEditorView::clickSelect(ox::Vec2 const&paneSize, ox::Vec2 const&clickPos) noexcept {
|
||||||
auto const pt = clickPoint(paneSize, clickPos);
|
auto const pt = clickPoint(paneSize, clickPos);
|
||||||
m_model.select(pt);
|
m_model.select(pt);
|
||||||
@ -96,7 +90,6 @@ void TileSheetEditorView::releaseMouseButton(TileSheetTool tool) noexcept {
|
|||||||
switch (tool) {
|
switch (tool) {
|
||||||
case TileSheetTool::Draw:
|
case TileSheetTool::Draw:
|
||||||
case TileSheetTool::Fill:
|
case TileSheetTool::Fill:
|
||||||
case TileSheetTool::Line:
|
|
||||||
m_model.endDrawCommand();
|
m_model.endDrawCommand();
|
||||||
break;
|
break;
|
||||||
case TileSheetTool::Select:
|
case TileSheetTool::Select:
|
||||||
|
@ -23,7 +23,6 @@ enum class TileSheetTool {
|
|||||||
Select,
|
Select,
|
||||||
Draw,
|
Draw,
|
||||||
Fill,
|
Fill,
|
||||||
Line,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
@ -35,8 +34,6 @@ constexpr auto toString(TileSheetTool t) noexcept {
|
|||||||
return "Draw";
|
return "Draw";
|
||||||
case TileSheetTool::Fill:
|
case TileSheetTool::Fill:
|
||||||
return "Fill";
|
return "Fill";
|
||||||
case TileSheetTool::Line:
|
|
||||||
return "Line";
|
|
||||||
case TileSheetTool::None:
|
case TileSheetTool::None:
|
||||||
return "None";
|
return "None";
|
||||||
}
|
}
|
||||||
@ -56,7 +53,7 @@ class TileSheetEditorView: public ox::SignalHandler {
|
|||||||
std::size_t m_palIdx = 0;
|
std::size_t m_palIdx = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TileSheetEditorView(studio::StudioContext &sctx, ox::StringViewCR path, studio::UndoStack &undoStack);
|
TileSheetEditorView(studio::StudioContext &sctx, ox::StringView path, studio::UndoStack &undoStack);
|
||||||
|
|
||||||
~TileSheetEditorView() override = default;
|
~TileSheetEditorView() override = default;
|
||||||
|
|
||||||
@ -68,8 +65,6 @@ class TileSheetEditorView: public ox::SignalHandler {
|
|||||||
|
|
||||||
void clickDraw(ox::Vec2 const&paneSize, ox::Vec2 const&clickPos) noexcept;
|
void clickDraw(ox::Vec2 const&paneSize, ox::Vec2 const&clickPos) noexcept;
|
||||||
|
|
||||||
void clickLine(ox::Vec2 const&paneSize, ox::Vec2 const&clickPos) noexcept;
|
|
||||||
|
|
||||||
void clickSelect(ox::Vec2 const&paneSize, ox::Vec2 const&clickPos) noexcept;
|
void clickSelect(ox::Vec2 const&paneSize, ox::Vec2 const&clickPos) noexcept;
|
||||||
|
|
||||||
void clickFill(ox::Vec2 const&paneSize, ox::Vec2 const&clickPos) noexcept;
|
void clickFill(ox::Vec2 const&paneSize, ox::Vec2 const&clickPos) noexcept;
|
||||||
|
@ -9,7 +9,6 @@ if(NOT WIN32)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(COMMAND OBJCOPY_FILE)
|
if(COMMAND OBJCOPY_FILE)
|
||||||
set(LOAD_KEEL_MODS FALSE)
|
|
||||||
set_target_properties(Nostalgia
|
set_target_properties(Nostalgia
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
LINK_FLAGS ${LINKER_FLAGS}
|
LINK_FLAGS ${LINKER_FLAGS}
|
||||||
@ -18,16 +17,8 @@ if(COMMAND OBJCOPY_FILE)
|
|||||||
|
|
||||||
OBJCOPY_FILE(Nostalgia)
|
OBJCOPY_FILE(Nostalgia)
|
||||||
#PADBIN_FILE(Nostalgia)
|
#PADBIN_FILE(Nostalgia)
|
||||||
else()
|
|
||||||
set(LOAD_KEEL_MODS TRUE)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_compile_definitions(
|
|
||||||
Nostalgia PRIVATE
|
|
||||||
OLYMPIC_LOAD_KEEL_MODULES=$<BOOL:${LOAD_KEEL_MODS}>
|
|
||||||
OLYMPIC_GUI_APP=1
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
Nostalgia
|
Nostalgia
|
||||||
NostalgiaKeelModules
|
NostalgiaKeelModules
|
||||||
|
@ -33,7 +33,7 @@ endif()
|
|||||||
|
|
||||||
install(
|
install(
|
||||||
FILES
|
FILES
|
||||||
ns_logo.icns
|
ns.icns
|
||||||
DESTINATION
|
DESTINATION
|
||||||
${NOSTALGIA_DIST_RESOURCES}/icons
|
${NOSTALGIA_DIST_RESOURCES}/icons
|
||||||
)
|
)
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<string>Nostalgia Studio</string>
|
<string>Nostalgia Studio</string>
|
||||||
|
|
||||||
<key>CFBundleIconFile</key>
|
<key>CFBundleIconFile</key>
|
||||||
<string>icons/ns_logo.icns</string>
|
<string>icons/ns.icns</string>
|
||||||
|
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>net.drinkingtea.nostalgia.studio</string>
|
<string>net.drinkingtea.nostalgia.studio</string>
|
||||||
@ -18,7 +18,7 @@
|
|||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
|
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>dev build</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 <gary@drinkingtea.net></string>
|
<string>Copyright (c) 2016-2023 Gary Talent <gary@drinkingtea.net></string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
Binary file not shown.
@ -15,8 +15,6 @@ constexpr auto K1HdrSz = 40;
|
|||||||
|
|
||||||
ox::Result<ox::UUID> readUuidHeader(ox::BufferView buff) noexcept;
|
ox::Result<ox::UUID> readUuidHeader(ox::BufferView buff) noexcept;
|
||||||
|
|
||||||
ox::Result<ox::UUID> regenerateUuidHeader(ox::Buffer &buff) noexcept;
|
|
||||||
|
|
||||||
ox::Error writeUuidHeader(ox::Writer_c auto &writer, ox::UUID const&uuid) noexcept {
|
ox::Error writeUuidHeader(ox::Writer_c auto &writer, ox::UUID const&uuid) noexcept {
|
||||||
OX_RETURN_ERROR(write(writer, "K1;"));
|
OX_RETURN_ERROR(write(writer, "K1;"));
|
||||||
OX_RETURN_ERROR(uuid.toString(writer));
|
OX_RETURN_ERROR(uuid.toString(writer));
|
||||||
|
@ -8,25 +8,15 @@ namespace keel {
|
|||||||
|
|
||||||
ox::Result<ox::UUID> readUuidHeader(ox::BufferView buff) noexcept {
|
ox::Result<ox::UUID> readUuidHeader(ox::BufferView buff) noexcept {
|
||||||
if (buff.size() < K1HdrSz) [[unlikely]] {
|
if (buff.size() < K1HdrSz) [[unlikely]] {
|
||||||
return ox::Error{1, "Insufficient data to contain complete Keel header"};
|
return ox::Error(1, "Insufficient data to contain complete Keel header");
|
||||||
}
|
}
|
||||||
constexpr ox::StringView k1Hdr = "K1;";
|
constexpr ox::StringView k1Hdr = "K1;";
|
||||||
if (k1Hdr != ox::StringView{buff.data(), k1Hdr.bytes()}) [[unlikely]] {
|
if (k1Hdr != ox::StringView(buff.data(), k1Hdr.bytes())) [[unlikely]] {
|
||||||
return ox::Error{2, "No Keel asset header data"};
|
return ox::Error(2, "No Keel asset header data");
|
||||||
}
|
}
|
||||||
return ox::UUID::fromString(ox::StringView(&buff[k1Hdr.bytes()], 36));
|
return ox::UUID::fromString(ox::StringView(&buff[k1Hdr.bytes()], 36));
|
||||||
}
|
}
|
||||||
|
|
||||||
ox::Result<ox::UUID> regenerateUuidHeader(ox::Buffer &buff) noexcept {
|
|
||||||
OX_RETURN_ERROR(readUuidHeader(buff));
|
|
||||||
OX_REQUIRE(id, ox::UUID::generate());
|
|
||||||
auto const str = id.toString();
|
|
||||||
for (size_t i = 0; i < ox::UUIDStr::cap(); ++i) {
|
|
||||||
buff[i + 3] = str[i];
|
|
||||||
}
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
ox::Result<ox::ModelObject> readAsset(ox::TypeStore &ts, ox::BufferView buff) noexcept {
|
ox::Result<ox::ModelObject> readAsset(ox::TypeStore &ts, ox::BufferView buff) noexcept {
|
||||||
std::size_t offset = 0;
|
std::size_t offset = 0;
|
||||||
if (!readUuidHeader(buff).error) {
|
if (!readUuidHeader(buff).error) {
|
||||||
|
@ -53,12 +53,10 @@ static ox::Error buildUuidMap(Context &ctx, ox::StringViewCR path) noexcept {
|
|||||||
OX_REQUIRE_M(filePath, ox::join("/", ox::Array<ox::StringView, 2>{path, f}));
|
OX_REQUIRE_M(filePath, ox::join("/", ox::Array<ox::StringView, 2>{path, f}));
|
||||||
OX_REQUIRE(stat, ctx.rom->stat(filePath));
|
OX_REQUIRE(stat, ctx.rom->stat(filePath));
|
||||||
if (stat.fileType == ox::FileType::NormalFile) {
|
if (stat.fileType == ox::FileType::NormalFile) {
|
||||||
ox::Array<char, K1HdrSz> buff;
|
OX_REQUIRE(data, ctx.rom->read(filePath));
|
||||||
OX_RETURN_ERROR(
|
auto const [hdr, err] = readAssetHeader(data);
|
||||||
ctx.rom->read(filePath, 0, buff.size(), buff));
|
|
||||||
auto const [uuid, err] = readUuidHeader(buff);
|
|
||||||
if (!err) {
|
if (!err) {
|
||||||
createUuidMapping(ctx, filePath, uuid);
|
createUuidMapping(ctx, filePath, hdr.uuid);
|
||||||
}
|
}
|
||||||
} else if (stat.fileType == ox::FileType::Directory) {
|
} else if (stat.fileType == ox::FileType::Directory) {
|
||||||
if (!beginsWith(f, ".")) {
|
if (!beginsWith(f, ".")) {
|
||||||
|
@ -5,7 +5,6 @@ add_library(
|
|||||||
deleteconfirmation.cpp
|
deleteconfirmation.cpp
|
||||||
filedialogmanager.cpp
|
filedialogmanager.cpp
|
||||||
main.cpp
|
main.cpp
|
||||||
makecopypopup.cpp
|
|
||||||
newdir.cpp
|
newdir.cpp
|
||||||
newmenu.cpp
|
newmenu.cpp
|
||||||
newproject.cpp
|
newproject.cpp
|
||||||
|
@ -1,81 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "makecopypopup.hpp"
|
|
||||||
|
|
||||||
namespace studio {
|
|
||||||
|
|
||||||
ox::Error MakeCopyPopup::open(ox::StringViewCR path) noexcept {
|
|
||||||
m_stage = Stage::Opening;
|
|
||||||
OX_REQUIRE(idx, ox::findIdx(path.rbegin(), path.rend(), '/'));
|
|
||||||
m_srcPath = path;
|
|
||||||
m_dirPath = substr(path, 0, idx + 1);
|
|
||||||
m_title = sfmt("Copy {}", path);
|
|
||||||
m_fileName = "";
|
|
||||||
m_errMsg = "";
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
void MakeCopyPopup::close() noexcept {
|
|
||||||
m_stage = Stage::Closed;
|
|
||||||
m_open = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MakeCopyPopup::isOpen() const noexcept {
|
|
||||||
return m_open;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MakeCopyPopup::draw(StudioContext const &ctx) noexcept {
|
|
||||||
switch (m_stage) {
|
|
||||||
case Stage::Closed:
|
|
||||||
break;
|
|
||||||
case Stage::Opening:
|
|
||||||
ImGui::OpenPopup(m_title.c_str());
|
|
||||||
m_stage = Stage::Open;
|
|
||||||
m_open = true;
|
|
||||||
[[fallthrough]];
|
|
||||||
case Stage::Open:
|
|
||||||
ig::centerNextWindow(ctx.tctx);
|
|
||||||
ImGui::SetNextWindowSize({250, 0});
|
|
||||||
constexpr auto modalFlags =
|
|
||||||
ImGuiWindowFlags_NoCollapse |
|
|
||||||
ImGuiWindowFlags_NoMove |
|
|
||||||
ImGuiWindowFlags_NoResize;
|
|
||||||
if (ImGui::BeginPopupModal(m_title.c_str(), &m_open, modalFlags)) {
|
|
||||||
if (ImGui::IsWindowAppearing()) {
|
|
||||||
ImGui::SetKeyboardFocusHere();
|
|
||||||
}
|
|
||||||
ig::InputText("Name", m_fileName);
|
|
||||||
if (ImGui::IsItemFocused() && ImGui::IsKeyPressed(ImGuiKey_Enter)) {
|
|
||||||
accept(ctx);
|
|
||||||
}
|
|
||||||
ImGui::Text("%s", m_errMsg.c_str());
|
|
||||||
bool open = true;
|
|
||||||
switch (ig::PopupControlsOkCancel(open)) {
|
|
||||||
case ig::PopupResponse::None:
|
|
||||||
break;
|
|
||||||
case ig::PopupResponse::OK:
|
|
||||||
accept(ctx);
|
|
||||||
break;
|
|
||||||
case ig::PopupResponse::Cancel:
|
|
||||||
close();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ImGui::EndPopup();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MakeCopyPopup::accept(StudioContext const &ctx) noexcept {
|
|
||||||
auto const p = sfmt("{}{}", m_dirPath, m_fileName);
|
|
||||||
if (!ctx.project->exists(p)) {
|
|
||||||
makeCopy.emit(m_srcPath, p);
|
|
||||||
close();
|
|
||||||
} else {
|
|
||||||
m_errMsg = sfmt("{} already exists", p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <ox/std/string.hpp>
|
|
||||||
|
|
||||||
#include <studio/context.hpp>
|
|
||||||
#include <studio/imguiutil.hpp>
|
|
||||||
|
|
||||||
namespace studio {
|
|
||||||
|
|
||||||
class MakeCopyPopup {
|
|
||||||
private:
|
|
||||||
enum class Stage {
|
|
||||||
Closed,
|
|
||||||
Opening,
|
|
||||||
Open,
|
|
||||||
};
|
|
||||||
Stage m_stage = Stage::Closed;
|
|
||||||
bool m_open{};
|
|
||||||
ox::String m_errMsg;
|
|
||||||
ox::String m_title{"Copy File"};
|
|
||||||
ox::String m_srcPath;
|
|
||||||
ox::String m_dirPath;
|
|
||||||
ox::IString<255> m_fileName;
|
|
||||||
|
|
||||||
public:
|
|
||||||
ox::Signal<ox::Error(ox::StringViewCR src, ox::StringViewCR dst)> makeCopy;
|
|
||||||
|
|
||||||
ox::Error open(ox::StringViewCR path) noexcept;
|
|
||||||
|
|
||||||
void close() noexcept;
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
bool isOpen() const noexcept;
|
|
||||||
|
|
||||||
void draw(StudioContext const &ctx) noexcept;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void accept(StudioContext const &ctx) noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@ -42,22 +42,19 @@ void ProjectExplorer::fileContextMenu(ox::StringViewCR path) const noexcept {
|
|||||||
if (ImGui::MenuItem("Rename")) {
|
if (ImGui::MenuItem("Rename")) {
|
||||||
renameItem.emit(path);
|
renameItem.emit(path);
|
||||||
}
|
}
|
||||||
if (ImGui::MenuItem("Make Copy")) {
|
|
||||||
makeCopy.emit(path);
|
|
||||||
}
|
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectExplorer::dirContextMenu(ox::StringViewCR path) const noexcept {
|
void ProjectExplorer::dirContextMenu(ox::StringViewCR path) const noexcept {
|
||||||
if (ImGui::BeginPopupContextItem("DirMenu", ImGuiPopupFlags_MouseButtonRight)) {
|
if (path.len() && ImGui::BeginPopupContextItem("DirMenu", ImGuiPopupFlags_MouseButtonRight)) {
|
||||||
if (ImGui::MenuItem("Add Item")) {
|
if (ImGui::MenuItem("Add Item")) {
|
||||||
addItem.emit(path);
|
addItem.emit(path);
|
||||||
}
|
}
|
||||||
if (ImGui::MenuItem("Add Directory")) {
|
if (ImGui::MenuItem("Add Directory")) {
|
||||||
addDir.emit(path);
|
addDir.emit(path);
|
||||||
}
|
}
|
||||||
if (path.len() && ImGui::MenuItem("Delete")) {
|
if (ImGui::MenuItem("Delete")) {
|
||||||
deleteItem.emit(path);
|
deleteItem.emit(path);
|
||||||
}
|
}
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
|
@ -20,7 +20,6 @@ class ProjectExplorer final: public FileExplorer {
|
|||||||
ox::Signal<ox::Error(ox::StringViewCR)> addDir;
|
ox::Signal<ox::Error(ox::StringViewCR)> addDir;
|
||||||
ox::Signal<ox::Error(ox::StringViewCR)> deleteItem;
|
ox::Signal<ox::Error(ox::StringViewCR)> deleteItem;
|
||||||
ox::Signal<ox::Error(ox::StringViewCR)> renameItem;
|
ox::Signal<ox::Error(ox::StringViewCR)> renameItem;
|
||||||
ox::Signal<ox::Error(ox::StringViewCR)> makeCopy;
|
|
||||||
ox::Signal<ox::Error(ox::StringViewCR src, ox::StringViewCR dst)> moveDir;
|
ox::Signal<ox::Error(ox::StringViewCR src, ox::StringViewCR dst)> moveDir;
|
||||||
ox::Signal<ox::Error(ox::StringViewCR src, ox::StringViewCR dst)> moveItem;
|
ox::Signal<ox::Error(ox::StringViewCR src, ox::StringViewCR dst)> moveItem;
|
||||||
|
|
||||||
|
@ -59,13 +59,11 @@ StudioUI::StudioUI(turbine::Context &ctx, ox::StringParam projectDataDir) noexce
|
|||||||
m_projectExplorer.addItem.connect(this, &StudioUI::addFile);
|
m_projectExplorer.addItem.connect(this, &StudioUI::addFile);
|
||||||
m_projectExplorer.deleteItem.connect(this, &StudioUI::deleteFile);
|
m_projectExplorer.deleteItem.connect(this, &StudioUI::deleteFile);
|
||||||
m_projectExplorer.renameItem.connect(this, &StudioUI::renameFile);
|
m_projectExplorer.renameItem.connect(this, &StudioUI::renameFile);
|
||||||
m_projectExplorer.makeCopy.connect(this, &StudioUI::makeCopyDlg);
|
|
||||||
m_projectExplorer.moveDir.connect(this, &StudioUI::queueDirMove);
|
m_projectExplorer.moveDir.connect(this, &StudioUI::queueDirMove);
|
||||||
m_projectExplorer.moveItem.connect(this, &StudioUI::queueFileMove);
|
m_projectExplorer.moveItem.connect(this, &StudioUI::queueFileMove);
|
||||||
m_renameFile.moveFile.connect(this, &StudioUI::queueFileMove);
|
m_renameFile.moveFile.connect(this, &StudioUI::queueFileMove);
|
||||||
m_newProject.finished.connect(this, &StudioUI::createOpenProject);
|
m_newProject.finished.connect(this, &StudioUI::createOpenProject);
|
||||||
m_newMenu.finished.connect(this, &StudioUI::openFile);
|
m_newMenu.finished.connect(this, &StudioUI::openFile);
|
||||||
m_closeFileConfirm.response.connect(this, &StudioUI::handleCloseFileResponse);
|
|
||||||
loadModules();
|
loadModules();
|
||||||
// open project and files
|
// open project and files
|
||||||
auto const [config, err] = studio::readConfig<StudioConfig>(keelCtx(m_tctx));
|
auto const [config, err] = studio::readConfig<StudioConfig>(keelCtx(m_tctx));
|
||||||
@ -136,8 +134,6 @@ void StudioUI::draw() noexcept {
|
|||||||
for (auto const p : m_popups) {
|
for (auto const p : m_popups) {
|
||||||
p->draw(m_sctx);
|
p->draw(m_sctx);
|
||||||
}
|
}
|
||||||
m_closeFileConfirm.draw(m_sctx);
|
|
||||||
m_copyFilePopup.draw(m_sctx);
|
|
||||||
}
|
}
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
handleKeyInput();
|
handleKeyInput();
|
||||||
@ -218,7 +214,7 @@ void StudioUI::drawTabs() noexcept {
|
|||||||
auto open = true;
|
auto open = true;
|
||||||
auto const unsavedChanges = e->unsavedChanges() ? ImGuiTabItemFlags_UnsavedDocument : 0;
|
auto const unsavedChanges = e->unsavedChanges() ? ImGuiTabItemFlags_UnsavedDocument : 0;
|
||||||
auto const selected = m_activeEditorUpdatePending == e.get() ? ImGuiTabItemFlags_SetSelected : 0;
|
auto const selected = m_activeEditorUpdatePending == e.get() ? ImGuiTabItemFlags_SetSelected : 0;
|
||||||
auto const flags = unsavedChanges | selected | ImGuiTabItemFlags_NoAssumedClosure;
|
auto const flags = unsavedChanges | selected;
|
||||||
if (ImGui::BeginTabItem(e->itemDisplayName().c_str(), &open, flags)) {
|
if (ImGui::BeginTabItem(e->itemDisplayName().c_str(), &open, flags)) {
|
||||||
if (m_activeEditor != e.get()) [[unlikely]] {
|
if (m_activeEditor != e.get()) [[unlikely]] {
|
||||||
m_activeEditor = e.get();
|
m_activeEditor = e.get();
|
||||||
@ -233,10 +229,7 @@ void StudioUI::drawTabs() noexcept {
|
|||||||
if (m_activeEditorOnLastDraw != e.get()) [[unlikely]] {
|
if (m_activeEditorOnLastDraw != e.get()) [[unlikely]] {
|
||||||
m_activeEditor->onActivated();
|
m_activeEditor->onActivated();
|
||||||
}
|
}
|
||||||
if (m_closeActiveTab) [[unlikely]] {
|
if (open) [[likely]] {
|
||||||
ImGui::SetTabItemClosed(e->itemDisplayName().c_str());
|
|
||||||
|
|
||||||
} else if (open) [[likely]] {
|
|
||||||
e->draw(m_sctx);
|
e->draw(m_sctx);
|
||||||
}
|
}
|
||||||
m_activeEditorOnLastDraw = e.get();
|
m_activeEditorOnLastDraw = e.get();
|
||||||
@ -244,39 +237,21 @@ void StudioUI::drawTabs() noexcept {
|
|||||||
ImGui::EndTabItem();
|
ImGui::EndTabItem();
|
||||||
}
|
}
|
||||||
if (!open) {
|
if (!open) {
|
||||||
if (e->unsavedChanges()) {
|
e->close();
|
||||||
m_closeFileConfirm.open();
|
if (m_activeEditor == (*it).get()) {
|
||||||
} else {
|
m_activeEditor = nullptr;
|
||||||
e->close();
|
}
|
||||||
if (m_activeEditor == (*it).get()) {
|
try {
|
||||||
m_activeEditor = nullptr;
|
OX_THROW_ERROR(m_editors.erase(it).moveTo(it));
|
||||||
}
|
} catch (ox::Exception const&ex) {
|
||||||
try {
|
oxErrf("Editor tab deletion failed: {} ({}:{})\n", ex.what(), ex.src.file_name(), ex.src.line());
|
||||||
OX_THROW_ERROR(m_editors.erase(it).moveTo(it));
|
} catch (std::exception const&ex) {
|
||||||
} catch (ox::Exception const&ex) {
|
oxErrf("Editor tab deletion failed: {}\n", ex.what());
|
||||||
oxErrf("Editor tab deletion failed: {} ({}:{})\n", ex.what(), ex.src.file_name(), ex.src.line());
|
|
||||||
} catch (std::exception const&ex) {
|
|
||||||
oxErrf("Editor tab deletion failed: {}\n", ex.what());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (m_closeActiveTab) [[unlikely]] {
|
|
||||||
if (m_activeEditor) {
|
|
||||||
auto const idx = find_if(
|
|
||||||
m_editors.begin(), m_editors.end(),
|
|
||||||
[this](ox::UPtr<BaseEditor> const &e) {
|
|
||||||
return m_activeEditor == e.get();
|
|
||||||
});
|
|
||||||
if (idx != m_editors.end()) {
|
|
||||||
oxLogError(m_editors.erase(idx.offset()).error);
|
|
||||||
}
|
|
||||||
m_activeEditor = nullptr;
|
|
||||||
}
|
|
||||||
m_closeActiveTab = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StudioUI::loadEditorMaker(EditorMaker const&editorMaker) noexcept {
|
void StudioUI::loadEditorMaker(EditorMaker const&editorMaker) noexcept {
|
||||||
@ -353,14 +328,6 @@ void StudioUI::handleKeyInput() noexcept {
|
|||||||
if (m_activeEditor && m_activeEditor->pasteEnabled()) {
|
if (m_activeEditor && m_activeEditor->pasteEnabled()) {
|
||||||
m_activeEditor->paste();
|
m_activeEditor->paste();
|
||||||
}
|
}
|
||||||
} else if (ImGui::IsKeyPressed(ImGuiKey_W)) {
|
|
||||||
if (m_activeEditor) {
|
|
||||||
if (m_activeEditor->unsavedChanges()) {
|
|
||||||
m_closeFileConfirm.open();
|
|
||||||
} else {
|
|
||||||
oxLogError(closeCurrentFile());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (ImGui::IsKeyPressed(ImGuiKey_X)) {
|
} else if (ImGui::IsKeyPressed(ImGuiKey_X)) {
|
||||||
if (m_activeEditor && m_activeEditor->cutEnabled()) {
|
if (m_activeEditor && m_activeEditor->cutEnabled()) {
|
||||||
m_activeEditor->cut();
|
m_activeEditor->cut();
|
||||||
@ -426,12 +393,13 @@ ox::Error StudioUI::handleMoveFile(ox::StringViewCR oldPath, ox::StringViewCR ne
|
|||||||
}
|
}
|
||||||
|
|
||||||
ox::Error StudioUI::handleDeleteFile(ox::StringViewCR path) noexcept {
|
ox::Error StudioUI::handleDeleteFile(ox::StringViewCR path) noexcept {
|
||||||
for (auto &e : m_editors) {
|
for (size_t i{}; auto &e : m_editors) {
|
||||||
if (path == e->itemPath()) {
|
if (path == e->itemPath()) {
|
||||||
|
oxLogError(m_editors.erase(i).error);
|
||||||
oxLogError(closeFile(path));
|
oxLogError(closeFile(path));
|
||||||
m_closeActiveTab = true;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
++i;
|
||||||
}
|
}
|
||||||
return m_projectExplorer.refreshProjectTreeModel();
|
return m_projectExplorer.refreshProjectTreeModel();
|
||||||
}
|
}
|
||||||
@ -453,7 +421,6 @@ ox::Error StudioUI::openProjectPath(ox::StringParam path) noexcept {
|
|||||||
m_sctx.project = m_project.get();
|
m_sctx.project = m_project.get();
|
||||||
turbine::setWindowTitle(m_tctx, ox::sfmt("{} - {}", keelCtx(m_tctx).appName, m_project->projectPath()));
|
turbine::setWindowTitle(m_tctx, ox::sfmt("{} - {}", keelCtx(m_tctx).appName, m_project->projectPath()));
|
||||||
m_deleteConfirmation.deleteFile.connect(m_sctx.project, &Project::deleteItem);
|
m_deleteConfirmation.deleteFile.connect(m_sctx.project, &Project::deleteItem);
|
||||||
m_copyFilePopup.makeCopy.connect(m_sctx.project, &Project::copyItem);
|
|
||||||
m_newDirDialog.newDir.connect(m_sctx.project, &Project::mkdir);
|
m_newDirDialog.newDir.connect(m_sctx.project, &Project::mkdir);
|
||||||
m_project->dirAdded.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel);
|
m_project->dirAdded.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel);
|
||||||
m_project->fileAdded.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel);
|
m_project->fileAdded.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel);
|
||||||
@ -516,28 +483,6 @@ ox::Error StudioUI::openFileActiveTab(ox::StringViewCR path, bool const makeActi
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ox::Error StudioUI::makeCopyDlg(ox::StringViewCR path) noexcept {
|
|
||||||
return m_copyFilePopup.open(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
ox::Error StudioUI::handleCloseFileResponse(ig::PopupResponse const response) noexcept {
|
|
||||||
if (response == ig::PopupResponse::OK && m_activeEditor) {
|
|
||||||
return closeCurrentFile();
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
ox::Error StudioUI::closeCurrentFile() noexcept {
|
|
||||||
for (auto &e : m_editors) {
|
|
||||||
if (m_activeEditor == e.get()) {
|
|
||||||
oxLogError(closeFile(e->itemPath()));
|
|
||||||
m_closeActiveTab = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
ox::Error StudioUI::closeFile(ox::StringViewCR path) noexcept {
|
ox::Error StudioUI::closeFile(ox::StringViewCR path) noexcept {
|
||||||
if (!m_openFiles.contains(path)) {
|
if (!m_openFiles.contains(path)) {
|
||||||
return {};
|
return {};
|
||||||
|
@ -9,14 +9,12 @@
|
|||||||
#include <ox/std/string.hpp>
|
#include <ox/std/string.hpp>
|
||||||
|
|
||||||
#include <studio/editor.hpp>
|
#include <studio/editor.hpp>
|
||||||
#include <studio/imguiutil.hpp>
|
|
||||||
#include <studio/module.hpp>
|
#include <studio/module.hpp>
|
||||||
#include <studio/project.hpp>
|
#include <studio/project.hpp>
|
||||||
#include <studio/task.hpp>
|
#include <studio/task.hpp>
|
||||||
|
|
||||||
#include "aboutpopup.hpp"
|
#include "aboutpopup.hpp"
|
||||||
#include "deleteconfirmation.hpp"
|
#include "deleteconfirmation.hpp"
|
||||||
#include "makecopypopup.hpp"
|
|
||||||
#include "newdir.hpp"
|
#include "newdir.hpp"
|
||||||
#include "newmenu.hpp"
|
#include "newmenu.hpp"
|
||||||
#include "newproject.hpp"
|
#include "newproject.hpp"
|
||||||
@ -42,14 +40,11 @@ class StudioUI: public ox::SignalHandler {
|
|||||||
BaseEditor *m_activeEditorOnLastDraw = nullptr;
|
BaseEditor *m_activeEditorOnLastDraw = nullptr;
|
||||||
BaseEditor *m_activeEditor = nullptr;
|
BaseEditor *m_activeEditor = nullptr;
|
||||||
BaseEditor *m_activeEditorUpdatePending = nullptr;
|
BaseEditor *m_activeEditorUpdatePending = nullptr;
|
||||||
bool m_closeActiveTab{};
|
|
||||||
ox::Vector<ox::Pair<ox::String>> m_queuedMoves;
|
ox::Vector<ox::Pair<ox::String>> m_queuedMoves;
|
||||||
ox::Vector<ox::Pair<ox::String>> m_queuedDirMoves;
|
ox::Vector<ox::Pair<ox::String>> m_queuedDirMoves;
|
||||||
NewMenu m_newMenu{keelCtx(m_tctx)};
|
NewMenu m_newMenu{keelCtx(m_tctx)};
|
||||||
DeleteConfirmation m_deleteConfirmation;
|
DeleteConfirmation m_deleteConfirmation;
|
||||||
NewDir m_newDirDialog;
|
NewDir m_newDirDialog;
|
||||||
ig::QuestionPopup m_closeFileConfirm{"Close File?", "This file has unsaved changes. Close?"};
|
|
||||||
MakeCopyPopup m_copyFilePopup;
|
|
||||||
RenameFile m_renameFile;
|
RenameFile m_renameFile;
|
||||||
NewProject m_newProject;
|
NewProject m_newProject;
|
||||||
AboutPopup m_aboutPopup;
|
AboutPopup m_aboutPopup;
|
||||||
@ -119,12 +114,6 @@ class StudioUI: public ox::SignalHandler {
|
|||||||
|
|
||||||
ox::Error openFileActiveTab(ox::StringViewCR path, bool makeActiveTab) noexcept;
|
ox::Error openFileActiveTab(ox::StringViewCR path, bool makeActiveTab) noexcept;
|
||||||
|
|
||||||
ox::Error makeCopyDlg(ox::StringViewCR path) noexcept;
|
|
||||||
|
|
||||||
ox::Error handleCloseFileResponse(ig::PopupResponse response) noexcept;
|
|
||||||
|
|
||||||
ox::Error closeCurrentFile() noexcept;
|
|
||||||
|
|
||||||
ox::Error closeFile(ox::StringViewCR path) noexcept;
|
ox::Error closeFile(ox::StringViewCR path) noexcept;
|
||||||
|
|
||||||
ox::Error queueDirMove(ox::StringParam src, ox::StringParam dst) noexcept;
|
ox::Error queueDirMove(ox::StringParam src, ox::StringParam dst) noexcept;
|
||||||
|
@ -225,18 +225,6 @@ PopupResponse PopupControlsOkCancel(
|
|||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
bool BeginPopup(turbine::Context &ctx, ox::CStringViewCR popupName, bool &show, ImVec2 const&sz = {285, 0});
|
bool BeginPopup(turbine::Context &ctx, ox::CStringViewCR popupName, bool &show, ImVec2 const&sz = {285, 0});
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param lbl
|
|
||||||
* @param list
|
|
||||||
* @param selectedIdx
|
|
||||||
* @return true if new value selected, false otherwise
|
|
||||||
*/
|
|
||||||
bool ComboBox(
|
|
||||||
ox::CStringView lbl,
|
|
||||||
ox::SpanView<ox::CStringView> list,
|
|
||||||
size_t &selectedIdx) noexcept;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param lbl
|
* @param lbl
|
||||||
@ -303,34 +291,6 @@ class FilePicker {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class QuestionPopup {
|
|
||||||
private:
|
|
||||||
enum class Stage {
|
|
||||||
Closed,
|
|
||||||
Opening,
|
|
||||||
Open,
|
|
||||||
};
|
|
||||||
Stage m_stage = Stage::Closed;
|
|
||||||
bool m_open{};
|
|
||||||
ox::String m_title;
|
|
||||||
ox::String m_question;
|
|
||||||
|
|
||||||
public:
|
|
||||||
ox::Signal<ox::Error(ig::PopupResponse)> response;
|
|
||||||
|
|
||||||
QuestionPopup(ox::StringParam title, ox::StringParam question) noexcept;
|
|
||||||
|
|
||||||
void open() noexcept;
|
|
||||||
|
|
||||||
void close() noexcept;
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
bool isOpen() const noexcept;
|
|
||||||
|
|
||||||
void draw(StudioContext &ctx, ImVec2 const &sz = {}) noexcept;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
bool mainWinHasFocus() noexcept;
|
bool mainWinHasFocus() noexcept;
|
||||||
|
|
||||||
|
@ -92,8 +92,6 @@ class Project: public ox::SignalHandler {
|
|||||||
|
|
||||||
ox::Result<ox::FileStat> stat(ox::StringViewCR path) const noexcept;
|
ox::Result<ox::FileStat> stat(ox::StringViewCR path) const noexcept;
|
||||||
|
|
||||||
ox::Error copyItem(ox::StringViewCR src, ox::StringViewCR dest) noexcept;
|
|
||||||
|
|
||||||
ox::Error moveItem(ox::StringViewCR src, ox::StringViewCR dest) noexcept;
|
ox::Error moveItem(ox::StringViewCR src, ox::StringViewCR dest) noexcept;
|
||||||
|
|
||||||
ox::Error moveDir(ox::StringViewCR src, ox::StringViewCR dest) noexcept;
|
ox::Error moveDir(ox::StringViewCR src, ox::StringViewCR dest) noexcept;
|
||||||
|
@ -90,25 +90,6 @@ bool BeginPopup(turbine::Context &ctx, ox::CStringViewCR popupName, bool &show,
|
|||||||
return ImGui::BeginPopupModal(popupName.c_str(), &show, modalFlags);
|
return ImGui::BeginPopupModal(popupName.c_str(), &show, modalFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ComboBox(
|
|
||||||
ox::CStringView lbl,
|
|
||||||
ox::SpanView<ox::CStringView> list,
|
|
||||||
size_t &selectedIdx) noexcept {
|
|
||||||
bool out{};
|
|
||||||
auto const first = selectedIdx < list.size() ? list[selectedIdx].c_str() : "";
|
|
||||||
if (ImGui::BeginCombo(lbl.c_str(), first, 0)) {
|
|
||||||
for (auto i = 0u; i < list.size(); ++i) {
|
|
||||||
const auto selected = (selectedIdx == i);
|
|
||||||
if (ImGui::Selectable(list[i].c_str(), selected) && selectedIdx != i) {
|
|
||||||
selectedIdx = i;
|
|
||||||
out = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui::EndCombo();
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ComboBox(
|
bool ComboBox(
|
||||||
ox::CStringView lbl,
|
ox::CStringView lbl,
|
||||||
ox::Span<const ox::String> list,
|
ox::Span<const ox::String> list,
|
||||||
@ -225,60 +206,6 @@ void FilePicker::show() noexcept {
|
|||||||
m_show = true;
|
m_show = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QuestionPopup::QuestionPopup(ox::StringParam title, ox::StringParam question) noexcept:
|
|
||||||
m_title{std::move(title)},
|
|
||||||
m_question{std::move(question)} {
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuestionPopup::open() noexcept {
|
|
||||||
m_stage = Stage::Opening;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuestionPopup::close() noexcept {
|
|
||||||
m_stage = Stage::Closed;
|
|
||||||
m_open = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QuestionPopup::isOpen() const noexcept {
|
|
||||||
return m_open;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuestionPopup::draw(StudioContext &ctx, ImVec2 const &sz) noexcept {
|
|
||||||
switch (m_stage) {
|
|
||||||
case Stage::Closed:
|
|
||||||
break;
|
|
||||||
case Stage::Opening:
|
|
||||||
ImGui::OpenPopup(m_title.c_str());
|
|
||||||
m_stage = Stage::Open;
|
|
||||||
m_open = true;
|
|
||||||
[[fallthrough]];
|
|
||||||
case Stage::Open:
|
|
||||||
centerNextWindow(ctx.tctx);
|
|
||||||
ImGui::SetNextWindowSize(static_cast<ImVec2>(sz));
|
|
||||||
constexpr auto modalFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
|
|
||||||
if (ImGui::BeginPopupModal(m_title.c_str(), &m_open, modalFlags)) {
|
|
||||||
ImGui::Text("%s", m_question.c_str());
|
|
||||||
auto const r = PopupControlsOkCancel(m_open, "Yes", "No");
|
|
||||||
switch (r) {
|
|
||||||
case PopupResponse::None:
|
|
||||||
break;
|
|
||||||
case PopupResponse::OK:
|
|
||||||
response.emit(r);
|
|
||||||
close();
|
|
||||||
break;
|
|
||||||
case PopupResponse::Cancel:
|
|
||||||
response.emit(r);
|
|
||||||
close();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ImGui::EndPopup();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool s_mainWinHasFocus{};
|
bool s_mainWinHasFocus{};
|
||||||
bool mainWinHasFocus() noexcept {
|
bool mainWinHasFocus() noexcept {
|
||||||
return s_mainWinHasFocus;
|
return s_mainWinHasFocus;
|
||||||
|
@ -97,14 +97,6 @@ ox::Result<ox::FileStat> Project::stat(ox::StringViewCR path) const noexcept {
|
|||||||
return m_fs.stat(path);
|
return m_fs.stat(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
ox::Error Project::copyItem(ox::StringViewCR src, ox::StringViewCR dest) noexcept {
|
|
||||||
OX_REQUIRE_M(buff, loadBuff(src));
|
|
||||||
OX_REQUIRE(id, keel::regenerateUuidHeader(buff));
|
|
||||||
OX_RETURN_ERROR(writeBuff(dest, buff));
|
|
||||||
createUuidMapping(m_kctx, dest, id);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
ox::Error Project::moveItem(ox::StringViewCR src, ox::StringViewCR dest) noexcept {
|
ox::Error Project::moveItem(ox::StringViewCR src, ox::StringViewCR dest) noexcept {
|
||||||
OX_RETURN_ERROR(m_fs.move(src, dest));
|
OX_RETURN_ERROR(m_fs.move(src, dest));
|
||||||
OX_RETURN_ERROR(keel::updatePath(m_kctx, src, dest));
|
OX_RETURN_ERROR(keel::updatePath(m_kctx, src, dest));
|
||||||
|
@ -7,7 +7,9 @@
|
|||||||
namespace studio {
|
namespace studio {
|
||||||
|
|
||||||
ox::Error UndoStack::push(ox::UPtr<UndoCommand> &&cmd) noexcept {
|
ox::Error UndoStack::push(ox::UPtr<UndoCommand> &&cmd) noexcept {
|
||||||
m_stack.resize(m_stackIdx);
|
for (auto const i = m_stackIdx; i < m_stack.size();) {
|
||||||
|
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());
|
||||||
@ -23,29 +25,22 @@ ox::Error UndoStack::push(ox::UPtr<UndoCommand> &&cmd) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ox::Error UndoStack::redo() noexcept {
|
ox::Error UndoStack::redo() noexcept {
|
||||||
while (m_stackIdx < m_stack.size()) {
|
if (m_stackIdx < m_stack.size()) {
|
||||||
auto const &c = m_stack[m_stackIdx];
|
auto &c = m_stack[m_stackIdx];
|
||||||
|
OX_RETURN_ERROR(c->redo());
|
||||||
++m_stackIdx;
|
++m_stackIdx;
|
||||||
if (!c->isObsolete()) {
|
redoTriggered.emit(c.get());
|
||||||
OX_RETURN_ERROR(c->redo());
|
changeTriggered.emit(c.get());
|
||||||
redoTriggered.emit(c.get());
|
|
||||||
changeTriggered.emit(c.get());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ox::Error UndoStack::undo() noexcept {
|
ox::Error UndoStack::undo() noexcept {
|
||||||
while (m_stackIdx) {
|
if (m_stackIdx) {
|
||||||
--m_stackIdx;
|
auto &c = m_stack[--m_stackIdx];
|
||||||
auto const &c = m_stack[m_stackIdx];
|
OX_RETURN_ERROR(c->undo());
|
||||||
if (!c->isObsolete()) {
|
undoTriggered.emit(c.get());
|
||||||
OX_RETURN_ERROR(c->undo());
|
changeTriggered.emit(c.get());
|
||||||
undoTriggered.emit(c.get());
|
|
||||||
changeTriggered.emit(c.get());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user