Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5eed819aa3 | |||
| 380c58936d | |||
| 03bd191664 | |||
| 62fe7ea67c | |||
| a64e6d3f6e | |||
| 80ae9bdedf | |||
| c18908a70c | |||
| 737f8f58f9 | |||
| 5b6ea9468b | |||
| 7128b7c83a | |||
| d560b7deaf | |||
| d4d66b5fea | |||
| 1948a1e701 | |||
| 8e52dc64d8 | |||
| 5c9b0be867 | |||
| e2395411bd | |||
| afb1bd23a3 | |||
| eb3364ce8e | |||
| 8899fc106f | |||
| 4a3d27fde8 | |||
| 51099f493b | |||
| fa9f3f8edf | |||
| 89fedc13b9 | |||
| 7b209583f5 | |||
| 8fd09f6fff | |||
| 261c620cec | |||
| f949d22b8e | |||
| 75039f6056 | |||
| 61e1a7aa72 | |||
| d2fe752846 | |||
| 406a53fd3a | |||
| 7d144eb18a | |||
| ac9bbefa82 | |||
| ef1c108b58 | |||
| 6a054cd970 | |||
| 9070e6e109 | |||
| 6cc6e9e7ed | |||
| de859bef77 | |||
| d1a3538e9a | |||
| d10a71f06d | |||
| 9593e7eef9 | |||
| 2f9b9c0842 | |||
| dce09b564c | |||
| 9f485d9496 | |||
| 71a20c00a0 | |||
| 8ea158f14d | |||
| e1d0c59d1c |
@@ -1 +1,2 @@
|
|||||||
type summary add --summary-string "${var.m_buff.m_items}" ox::String
|
type summary add --summary-string "${var.m_buff.m_items}" ox::String
|
||||||
|
type summary add --summary-string "${var.m_value.m_buff.m_items}" ox::StringParam
|
||||||
|
|||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.19)
|
cmake_minimum_required(VERSION 3.25)
|
||||||
set(CMAKE_POLICY_DEFAULT_CMP0110 NEW) # requires CMake 3.19
|
set(CMAKE_POLICY_DEFAULT_CMP0110 NEW) # requires CMake 3.19
|
||||||
|
|
||||||
if(BUILDCORE_TARGET STREQUAL "gba")
|
if(BUILDCORE_TARGET STREQUAL "gba")
|
||||||
@@ -52,7 +52,7 @@ if(NOT BUILDCORE_TARGET STREQUAL "gba")
|
|||||||
set(GLFW_BUILD_TESTS OFF)
|
set(GLFW_BUILD_TESTS OFF)
|
||||||
set(GLFW_BUILD_DOCS OFF)
|
set(GLFW_BUILD_DOCS OFF)
|
||||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||||
set(GLFW_BUILD_WAYLAND OFF)
|
set(GLFW_BUILD_WAYLAND ON)
|
||||||
endif()
|
endif()
|
||||||
add_subdirectory(deps/glfw)
|
add_subdirectory(deps/glfw)
|
||||||
add_subdirectory(deps/glutils)
|
add_subdirectory(deps/glutils)
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ git-push-ox:
|
|||||||
git subtree push --prefix=deps/oxlib ox-master master
|
git subtree push --prefix=deps/oxlib ox-master master
|
||||||
|
|
||||||
.PHONY: pkg-gba
|
.PHONY: pkg-gba
|
||||||
pkg-gba: build-pack build-gba-player
|
pkg-gba: build-studio build-gba-player
|
||||||
${BC_CMD_ENVRUN} ${BC_PY3} ./util/scripts/pkg-gba.py sample_project ${BC_VAR_PROJECT_NAME_CAP}
|
${BC_CMD_ENVRUN} ${BC_PY3} ./util/scripts/pkg-gba.py ${PROJECT_STUDIO} sample_project ${BC_VAR_PROJECT_NAME_CAP}
|
||||||
|
|
||||||
.PHONY: pkg-mac
|
.PHONY: pkg-mac
|
||||||
pkg-mac: install
|
pkg-mac: install
|
||||||
@@ -47,11 +47,6 @@ build-gba-player:
|
|||||||
build-player:
|
build-player:
|
||||||
${BC_CMD_CMAKE_BUILD} ${BC_VAR_BUILD_PATH} ${BC_VAR_PROJECT_NAME_CAP}
|
${BC_CMD_CMAKE_BUILD} ${BC_VAR_BUILD_PATH} ${BC_VAR_PROJECT_NAME_CAP}
|
||||||
|
|
||||||
.PHONY: build-pack
|
|
||||||
build-pack:
|
|
||||||
cmake --build ./build/${BC_VAR_CURRENT_BUILD} --target ${BC_VAR_PROJECT_NAME}-pack
|
|
||||||
|
|
||||||
|
|
||||||
.PHONY: run
|
.PHONY: run
|
||||||
run: build-player
|
run: build-player
|
||||||
${PROJECT_PLAYER} sample_project
|
${PROJECT_PLAYER} sample_project
|
||||||
|
|||||||
+1
-2
@@ -129,8 +129,7 @@ Error writeClaw(
|
|||||||
if (fmt == ClawFormat::Metal) {
|
if (fmt == ClawFormat::Metal) {
|
||||||
OX_RETURN_ERROR(writeMC(writer, obj));
|
OX_RETURN_ERROR(writeMC(writer, obj));
|
||||||
} else if (fmt == ClawFormat::Organic) {
|
} else if (fmt == ClawFormat::Organic) {
|
||||||
OX_REQUIRE(data, writeOC(obj));
|
OX_RETURN_ERROR(writeOC(writer, obj));
|
||||||
OX_RETURN_ERROR(writer.write(data.data(), data.size()));
|
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if (fmt != ClawFormat::Metal) {
|
if (fmt != ClawFormat::Metal) {
|
||||||
|
|||||||
+3
-1
@@ -28,8 +28,10 @@ String PassThroughFS::basePath() const noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Error PassThroughFS::mkdir(StringViewCR path, bool recursive) noexcept {
|
Error PassThroughFS::mkdir(StringViewCR path, bool recursive) noexcept {
|
||||||
|
auto const cleanPath =
|
||||||
|
endsWith(path, '/') ? substr(path, 0, path.size() - 1) : path;
|
||||||
bool success = false;
|
bool success = false;
|
||||||
const auto p = m_path / stripSlash(path);
|
auto p = cleanPath.size() ? m_path / stripSlash(cleanPath) : m_path;
|
||||||
const auto u8p = p.u8string();
|
const auto u8p = p.u8string();
|
||||||
oxTrace("ox.fs.PassThroughFS.mkdir", std::bit_cast<const char*>(u8p.c_str()));
|
oxTrace("ox.fs.PassThroughFS.mkdir", std::bit_cast<const char*>(u8p.c_str()));
|
||||||
if (recursive) {
|
if (recursive) {
|
||||||
|
|||||||
+1
-1
@@ -282,7 +282,7 @@ Error readOC(BufferView const buff, auto &val) noexcept {
|
|||||||
try {
|
try {
|
||||||
Json::Value doc;
|
Json::Value doc;
|
||||||
Json::CharReaderBuilder parserBuilder;
|
Json::CharReaderBuilder parserBuilder;
|
||||||
auto parser = UPtr<Json::CharReader>(parserBuilder.newCharReader());
|
auto const parser = UPtr<Json::CharReader>(parserBuilder.newCharReader());
|
||||||
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
||||||
if (!parser->parse(buff.data(), buff.data() + buff.size(), &doc, nullptr)) {
|
if (!parser->parse(buff.data(), buff.data() + buff.size(), &doc, nullptr)) {
|
||||||
OX_ALLOW_UNSAFE_BUFFERS_END
|
OX_ALLOW_UNSAFE_BUFFERS_END
|
||||||
|
|||||||
+13
-2
@@ -28,6 +28,7 @@ namespace ox {
|
|||||||
|
|
||||||
class OrganicClawWriter {
|
class OrganicClawWriter {
|
||||||
|
|
||||||
|
friend Error writeOC(Writer_c auto &writer, auto const &val) noexcept;
|
||||||
friend Result<Buffer> writeOC(const auto &val) noexcept;
|
friend Result<Buffer> writeOC(const auto &val) noexcept;
|
||||||
friend Result<String> writeOCString(const auto &val) noexcept;
|
friend Result<String> writeOCString(const auto &val) noexcept;
|
||||||
|
|
||||||
@@ -254,9 +255,19 @@ Error OrganicClawWriter::field(CString key, UnionView<U, force> val) noexcept {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Error writeOC(Writer_c auto &writer, auto const &val) noexcept {
|
||||||
|
OrganicClawWriter ocWriter;
|
||||||
|
ModelHandlerInterface handler(ocWriter);
|
||||||
|
OX_RETURN_ERROR(model(&handler, &val));
|
||||||
|
Json::StreamWriterBuilder const jsonBuilder;
|
||||||
|
auto const str = Json::writeString(jsonBuilder, ocWriter.m_json);
|
||||||
|
OX_RETURN_ERROR(writer.write(str.data(), str.size()));
|
||||||
|
return writer.put('\0');
|
||||||
|
}
|
||||||
|
|
||||||
Result<Buffer> writeOC(auto const &val) noexcept {
|
Result<Buffer> writeOC(auto const &val) noexcept {
|
||||||
OrganicClawWriter writer;
|
OrganicClawWriter writer;
|
||||||
ModelHandlerInterface<OrganicClawWriter, OpType::Write> handler(writer);
|
ModelHandlerInterface handler(writer);
|
||||||
OX_RETURN_ERROR(model(&handler, &val));
|
OX_RETURN_ERROR(model(&handler, &val));
|
||||||
Json::StreamWriterBuilder const jsonBuilder;
|
Json::StreamWriterBuilder const jsonBuilder;
|
||||||
auto const str = Json::writeString(jsonBuilder, writer.m_json);
|
auto const str = Json::writeString(jsonBuilder, writer.m_json);
|
||||||
@@ -270,7 +281,7 @@ Result<Buffer> writeOC(auto const &val) noexcept {
|
|||||||
|
|
||||||
Result<String> writeOCString(auto const &val) noexcept {
|
Result<String> writeOCString(auto const &val) noexcept {
|
||||||
OrganicClawWriter writer;
|
OrganicClawWriter writer;
|
||||||
ModelHandlerInterface<OrganicClawWriter, OpType::Write> handler(writer);
|
ModelHandlerInterface handler(writer);
|
||||||
OX_RETURN_ERROR(model(&handler, &val));
|
OX_RETURN_ERROR(model(&handler, &val));
|
||||||
Json::StreamWriterBuilder const jsonBuilder;
|
Json::StreamWriterBuilder const jsonBuilder;
|
||||||
auto const str = Json::writeString(jsonBuilder, writer.m_json);
|
auto const str = Json::writeString(jsonBuilder, writer.m_json);
|
||||||
|
|||||||
+3
-3
@@ -348,15 +348,15 @@ struct [[nodiscard]] Result {
|
|||||||
constexpr Result reoriginate(
|
constexpr Result reoriginate(
|
||||||
ErrorCode const pErrCode,
|
ErrorCode const pErrCode,
|
||||||
CString const pMsg = nullptr,
|
CString const pMsg = nullptr,
|
||||||
std::source_location const &pSrc = std::source_location::current()) const && noexcept {
|
std::source_location const &pSrc = std::source_location::current()) && noexcept {
|
||||||
if (error) {
|
if (error) {
|
||||||
return {std::move(value), Error{pErrCode, pMsg, pSrc}};
|
return {std::move(value), Error{pErrCode, pMsg, pSrc}};
|
||||||
}
|
}
|
||||||
return Error{};
|
return {std::move(value)};
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr Result reoriginate(
|
constexpr Result reoriginate(
|
||||||
std::source_location const &pSrc = std::source_location::current()) const && noexcept {
|
std::source_location const &pSrc = std::source_location::current()) && noexcept {
|
||||||
return {std::move(value), Error{error.errCode, error.msg, pSrc}};
|
return {std::move(value), Error{error.errCode, error.msg, pSrc}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -38,11 +38,11 @@ constexpr StringView substr(StringViewCR str, std::size_t const start, std::size
|
|||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
constexpr char toUpper(char const c) noexcept {
|
constexpr char toUpper(char const c) noexcept {
|
||||||
return c & 0b1101'1111;
|
return c & static_cast<char>(0b1101'1111);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
constexpr int caseInsensitiveEquals(ox::StringViewCR a, ox::StringViewCR b) noexcept {
|
constexpr int caseInsensitiveStrCmp(StringViewCR a, StringViewCR b) noexcept {
|
||||||
auto const sz = ox::min(a.size(), b.size());
|
auto const sz = ox::min(a.size(), b.size());
|
||||||
for (size_t i{}; i < sz; ++i) {
|
for (size_t i{}; i < sz; ++i) {
|
||||||
auto const ac = toUpper(a[i]);
|
auto const ac = toUpper(a[i]);
|
||||||
|
|||||||
+14
@@ -54,10 +54,21 @@ struct VectorAllocator {
|
|||||||
// this totally idiotic redundant check (&& count <= Size) is required to address a bug in devkitARM,
|
// this totally idiotic redundant check (&& count <= Size) is required to address a bug in devkitARM,
|
||||||
// try removing it later
|
// try removing it later
|
||||||
if (!std::is_constant_evaluated()) {
|
if (!std::is_constant_evaluated()) {
|
||||||
|
#if defined(__GNUC__) && !defined(__clang__)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wstringop-overflow="
|
||||||
|
#endif
|
||||||
if (cap <= m_data.size() && count <= m_data.size()) {
|
if (cap <= m_data.size() && count <= m_data.size()) {
|
||||||
for (auto i = 0u; i < count; ++i) {
|
for (auto i = 0u; i < count; ++i) {
|
||||||
auto const srcItem = std::launder(reinterpret_cast<T*>(&src->m_data[i]));
|
auto const srcItem = std::launder(reinterpret_cast<T*>(&src->m_data[i]));
|
||||||
|
#if defined(__GNUC__) && __GNUC__ >= 12
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wstringop-overflow="
|
||||||
|
#endif
|
||||||
new (&m_data[i]) T(std::move(*srcItem));
|
new (&m_data[i]) T(std::move(*srcItem));
|
||||||
|
#if defined(__GNUC__) && __GNUC__ >= 12
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
if (count) {
|
if (count) {
|
||||||
*items = std::launder(reinterpret_cast<T*>(m_data.data()));
|
*items = std::launder(reinterpret_cast<T*>(m_data.data()));
|
||||||
@@ -65,6 +76,9 @@ struct VectorAllocator {
|
|||||||
*items = reinterpret_cast<T*>(m_data.data());
|
*items = reinterpret_cast<T*>(m_data.data());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if defined(__GNUC__) && !defined(__clang__)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-1
@@ -1,6 +1,9 @@
|
|||||||
# NEXT
|
# d2026.05.0
|
||||||
|
|
||||||
|
* Add HiDPI support
|
||||||
* Add Get Info file dialog option in project explorer
|
* Add Get Info file dialog option in project explorer
|
||||||
|
* Fix Navigate Back to not require a double Forward when going all the way back
|
||||||
|
* Fix issue with config data not being saved correctly on first run
|
||||||
|
|
||||||
# d2025.07.0
|
# d2025.07.0
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ if(${NOSTALGIA_BUILD_PLAYER})
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT BUILDCORE_TARGET STREQUAL "gba")
|
if(NOT BUILDCORE_TARGET STREQUAL "gba")
|
||||||
add_subdirectory(tools)
|
|
||||||
if(${NOSTALGIA_BUILD_STUDIO_APP})
|
if(${NOSTALGIA_BUILD_STUDIO_APP})
|
||||||
add_subdirectory(studio)
|
add_subdirectory(studio)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <studio/studio.hpp>
|
#include <studio/undocommand.hpp>
|
||||||
|
|
||||||
#include <nostalgia/gfx/palette.hpp>
|
#include <nostalgia/gfx/palette.hpp>
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <studio/studio.hpp>
|
#include <studio/undocommand.hpp>
|
||||||
|
|
||||||
#include <nostalgia/gfx/palette.hpp>
|
#include <nostalgia/gfx/palette.hpp>
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <studio/studio.hpp>
|
#include <studio/undocommand.hpp>
|
||||||
|
|
||||||
#include <nostalgia/gfx/palette.hpp>
|
#include <nostalgia/gfx/palette.hpp>
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <studio/studio.hpp>
|
#include <studio/undocommand.hpp>
|
||||||
|
|
||||||
#include <nostalgia/gfx/palette.hpp>
|
#include <nostalgia/gfx/palette.hpp>
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <studio/studio.hpp>
|
#include <studio/undocommand.hpp>
|
||||||
|
|
||||||
#include <nostalgia/gfx/palette.hpp>
|
#include <nostalgia/gfx/palette.hpp>
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <studio/studio.hpp>
|
#include <studio/undocommand.hpp>
|
||||||
|
|
||||||
#include <nostalgia/gfx/palette.hpp>
|
#include <nostalgia/gfx/palette.hpp>
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <studio/studio.hpp>
|
#include <studio/undocommand.hpp>
|
||||||
|
|
||||||
#include <nostalgia/gfx/palette.hpp>
|
#include <nostalgia/gfx/palette.hpp>
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <studio/studio.hpp>
|
#include <studio/undocommand.hpp>
|
||||||
|
|
||||||
#include <nostalgia/gfx/palette.hpp>
|
#include <nostalgia/gfx/palette.hpp>
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <studio/studio.hpp>
|
#include <studio/undocommand.hpp>
|
||||||
|
|
||||||
#include <nostalgia/gfx/palette.hpp>
|
#include <nostalgia/gfx/palette.hpp>
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <studio/studio.hpp>
|
#include <studio/undocommand.hpp>
|
||||||
|
|
||||||
#include <nostalgia/gfx/palette.hpp>
|
#include <nostalgia/gfx/palette.hpp>
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <studio/studio.hpp>
|
#include <studio/undocommand.hpp>
|
||||||
|
|
||||||
#include <nostalgia/gfx/palette.hpp>
|
#include <nostalgia/gfx/palette.hpp>
|
||||||
|
|
||||||
|
|||||||
@@ -45,11 +45,11 @@ OX_MODEL_BEGIN(PageDragDrop)
|
|||||||
OX_MODEL_FIELD(page)
|
OX_MODEL_FIELD(page)
|
||||||
OX_MODEL_END()
|
OX_MODEL_END()
|
||||||
|
|
||||||
void PaletteEditorImGui::PageRenameDialog::draw(turbine::Context &tctx) noexcept {
|
void PaletteEditorImGui::PageRenameDialog::draw() noexcept {
|
||||||
if (!m_show) {
|
if (!m_show) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (ig::BeginPopup(tctx, "Rename Page", m_show)) {
|
if (ig::BeginPopup("Rename Page", m_show)) {
|
||||||
if (ImGui::IsWindowAppearing()) {
|
if (ImGui::IsWindowAppearing()) {
|
||||||
ImGui::SetKeyboardFocusHere();
|
ImGui::SetKeyboardFocusHere();
|
||||||
}
|
}
|
||||||
@@ -71,26 +71,26 @@ void PaletteEditorImGui::PageRenameDialog::draw(turbine::Context &tctx) noexcept
|
|||||||
PaletteEditorImGui::PaletteEditorImGui(studio::Context &sctx, ox::StringParam path):
|
PaletteEditorImGui::PaletteEditorImGui(studio::Context &sctx, ox::StringParam path):
|
||||||
Editor(sctx, std::move(path)),
|
Editor(sctx, std::move(path)),
|
||||||
m_sctx(sctx),
|
m_sctx(sctx),
|
||||||
m_tctx(sctx.tctx),
|
|
||||||
m_pal(m_sctx.project->loadObj<Palette>(itemPath()).unwrapThrow()) {
|
m_pal(m_sctx.project->loadObj<Palette>(itemPath()).unwrapThrow()) {
|
||||||
undoStack()->changeTriggered.connect(this, &PaletteEditorImGui::handleCommand);
|
undoStack()->changeTriggered.connect(this, &PaletteEditorImGui::handleCommand);
|
||||||
m_pageRenameDlg.inputSubmitted.connect(this, &PaletteEditorImGui::renamePage);
|
m_pageRenameDlg.inputSubmitted.connect(this, &PaletteEditorImGui::renamePage);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PaletteEditorImGui::draw(studio::Context&) noexcept {
|
void PaletteEditorImGui::draw(studio::Context&) noexcept {
|
||||||
|
auto const scale = ig::dpiScale();
|
||||||
auto const paneSize = ImGui::GetContentRegionAvail();
|
auto const paneSize = ImGui::GetContentRegionAvail();
|
||||||
{
|
{
|
||||||
ImGui::BeginChild("Pages", {280, paneSize.y}, true);
|
ImGui::BeginChild("Pages", {scale * 280, paneSize.y}, true);
|
||||||
drawPagesEditor();
|
drawPagesEditor();
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
{
|
{
|
||||||
ImGui::BeginChild("Colors", {-1, paneSize.y}, true);
|
ImGui::BeginChild("Colors", {scale * -1, paneSize.y}, true);
|
||||||
drawColorsEditor();
|
drawColorsEditor();
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
}
|
}
|
||||||
m_pageRenameDlg.draw(m_tctx);
|
m_pageRenameDlg.draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
ox::Error PaletteEditorImGui::saveItem() noexcept {
|
ox::Error PaletteEditorImGui::saveItem() noexcept {
|
||||||
@@ -165,7 +165,7 @@ void PaletteEditorImGui::drawColorsEditor() noexcept {
|
|||||||
static constexpr auto toolbarHeight = 40;
|
static constexpr auto toolbarHeight = 40;
|
||||||
{
|
{
|
||||||
auto constexpr sz = ImVec2{70, 24};
|
auto constexpr sz = ImVec2{70, 24};
|
||||||
if (ImGui::Button("Add", sz)) {
|
if (ig::PushButton("Add", sz)) {
|
||||||
auto const colorSz = colorCnt(m_pal, m_page);
|
auto const colorSz = colorCnt(m_pal, m_page);
|
||||||
constexpr Color16 c = 0;
|
constexpr Color16 c = 0;
|
||||||
std::ignore = pushCommand<AddColorCommand>(m_pal, c, colorSz);
|
std::ignore = pushCommand<AddColorCommand>(m_pal, c, colorSz);
|
||||||
@@ -173,7 +173,7 @@ void PaletteEditorImGui::drawColorsEditor() noexcept {
|
|||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::BeginDisabled(m_selectedColorRow >= colorCnt(m_pal, m_page));
|
ImGui::BeginDisabled(m_selectedColorRow >= colorCnt(m_pal, m_page));
|
||||||
{
|
{
|
||||||
if (ImGui::Button("Remove", sz)) {
|
if (ig::PushButton("Remove", sz)) {
|
||||||
std::ignore = pushCommand<RemoveColorCommand>(m_pal, m_selectedColorRow);
|
std::ignore = pushCommand<RemoveColorCommand>(m_pal, m_selectedColorRow);
|
||||||
m_selectedColorRow = ox::min(colorCnt(m_pal, m_page) - 1, m_selectedColorRow);
|
m_selectedColorRow = ox::min(colorCnt(m_pal, m_page) - 1, m_selectedColorRow);
|
||||||
colorEditor = m_selectedColorRow < colorCnt(m_pal, m_page);
|
colorEditor = m_selectedColorRow < colorCnt(m_pal, m_page);
|
||||||
@@ -183,17 +183,18 @@ void PaletteEditorImGui::drawColorsEditor() noexcept {
|
|||||||
}
|
}
|
||||||
auto const tblWidth = (colorsSz.x - static_cast<float>(colorEditorWidth) - 8.f)
|
auto const tblWidth = (colorsSz.x - static_cast<float>(colorEditorWidth) - 8.f)
|
||||||
* static_cast<float>(colorEditor);
|
* static_cast<float>(colorEditor);
|
||||||
|
auto const scale = ig::dpiScale();
|
||||||
ImGui::BeginTable(
|
ImGui::BeginTable(
|
||||||
"Colors",
|
"Colors",
|
||||||
6,
|
6,
|
||||||
tableFlags,
|
tableFlags,
|
||||||
{tblWidth, colorsSz.y - (toolbarHeight + 5)});
|
{tblWidth * scale, colorsSz.y - (toolbarHeight + 5) * scale});
|
||||||
{
|
{
|
||||||
ImGui::TableSetupColumn("Idx", ImGuiTableColumnFlags_WidthFixed, 25);
|
ImGui::TableSetupColumn("Idx", ImGuiTableColumnFlags_WidthFixed, 25 * scale);
|
||||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 100);
|
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 100 * scale);
|
||||||
ImGui::TableSetupColumn("Red", ImGuiTableColumnFlags_WidthFixed, 40);
|
ImGui::TableSetupColumn("Red", ImGuiTableColumnFlags_WidthFixed, 40 * scale);
|
||||||
ImGui::TableSetupColumn("Green", ImGuiTableColumnFlags_WidthFixed, 40);
|
ImGui::TableSetupColumn("Green", ImGuiTableColumnFlags_WidthFixed, 40 * scale);
|
||||||
ImGui::TableSetupColumn("Blue", ImGuiTableColumnFlags_WidthFixed, 40);
|
ImGui::TableSetupColumn("Blue", ImGuiTableColumnFlags_WidthFixed, 40 * scale);
|
||||||
ImGui::TableSetupColumn("Preview", ImGuiTableColumnFlags_NoHide);
|
ImGui::TableSetupColumn("Preview", ImGuiTableColumnFlags_NoHide);
|
||||||
ImGui::TableHeadersRow();
|
ImGui::TableHeadersRow();
|
||||||
if (m_page < m_pal.pages.size()) {
|
if (m_page < m_pal.pages.size()) {
|
||||||
@@ -236,10 +237,11 @@ void PaletteEditorImGui::drawColorsEditor() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PaletteEditorImGui::drawPagesEditor() noexcept {
|
void PaletteEditorImGui::drawPagesEditor() noexcept {
|
||||||
|
auto const scale = ig::dpiScale();
|
||||||
constexpr auto tableFlags = ImGuiTableFlags_RowBg;
|
constexpr auto tableFlags = ImGuiTableFlags_RowBg;
|
||||||
auto const paneSz = ImGui::GetContentRegionAvail();
|
auto const paneSz = ImGui::GetContentRegionAvail();
|
||||||
constexpr auto toolbarHeight = 40;
|
constexpr auto toolbarHeight = 40;
|
||||||
auto const btnSz = ImVec2{paneSz.x / 4 - 5.5f, 24};
|
auto const btnSz = ImVec2{paneSz.x / 4 - 5.5f * scale, 24 * scale};
|
||||||
if (ImGui::Button("Add", btnSz)) {
|
if (ImGui::Button("Add", btnSz)) {
|
||||||
if (m_pal.pages.empty()) {
|
if (m_pal.pages.empty()) {
|
||||||
std::ignore = pushCommand<AddPageCommand>(m_pal);
|
std::ignore = pushCommand<AddPageCommand>(m_pal);
|
||||||
@@ -265,7 +267,7 @@ void PaletteEditorImGui::drawPagesEditor() noexcept {
|
|||||||
"PageSelect",
|
"PageSelect",
|
||||||
2,
|
2,
|
||||||
tableFlags,
|
tableFlags,
|
||||||
{paneSz.x, paneSz.y - (toolbarHeight + 5)});
|
{paneSz.x, paneSz.y - (toolbarHeight + 5) * scale});
|
||||||
{
|
{
|
||||||
ImGui::TableSetupColumn("Page", ImGuiTableColumnFlags_WidthFixed, 60);
|
ImGui::TableSetupColumn("Page", ImGuiTableColumnFlags_WidthFixed, 60);
|
||||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 200);
|
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 200);
|
||||||
|
|||||||
@@ -29,10 +29,9 @@ class PaletteEditorImGui: public studio::Editor {
|
|||||||
}
|
}
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
constexpr bool isOpen() const noexcept { return m_show; }
|
constexpr bool isOpen() const noexcept { return m_show; }
|
||||||
void draw(turbine::Context &tctx) noexcept;
|
void draw() noexcept;
|
||||||
} m_pageRenameDlg;
|
} m_pageRenameDlg;
|
||||||
studio::Context &m_sctx;
|
studio::Context &m_sctx;
|
||||||
turbine::Context &m_tctx;
|
|
||||||
Palette m_pal;
|
Palette m_pal;
|
||||||
size_t m_selectedColorRow = 0;
|
size_t m_selectedColorRow = 0;
|
||||||
size_t m_page = 0;
|
size_t m_page = 0;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ struct SubSheetRef {
|
|||||||
TileSheet::SubSheetIdx subsheet{};
|
TileSheet::SubSheetIdx subsheet{};
|
||||||
};
|
};
|
||||||
|
|
||||||
OX_MODEL_BEGIN(SubSheetRef)
|
static OX_MODEL_BEGIN(SubSheetRef)
|
||||||
OX_MODEL_FIELD(subsheet)
|
OX_MODEL_FIELD(subsheet)
|
||||||
OX_MODEL_END()
|
OX_MODEL_END()
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ struct TileSheetEditorConfig {
|
|||||||
TileSheet::SubSheetIdx activeSubsheet{};
|
TileSheet::SubSheetIdx activeSubsheet{};
|
||||||
};
|
};
|
||||||
|
|
||||||
OX_MODEL_BEGIN(TileSheetEditorConfig)
|
static OX_MODEL_BEGIN(TileSheetEditorConfig)
|
||||||
OX_MODEL_FIELD_RENAME(activeSubsheet, active_subsheet)
|
OX_MODEL_FIELD_RENAME(activeSubsheet, active_subsheet)
|
||||||
OX_MODEL_END()
|
OX_MODEL_END()
|
||||||
|
|
||||||
@@ -149,27 +149,30 @@ void TileSheetEditorImGui::draw(studio::Context&) noexcept {
|
|||||||
if (ImGui::IsKeyDown(ImGuiKey_ModCtrl) && !m_palPathFocused) {
|
if (ImGui::IsKeyDown(ImGuiKey_ModCtrl) && !m_palPathFocused) {
|
||||||
if (ImGui::IsKeyPressed(ImGuiKey_A)) {
|
if (ImGui::IsKeyPressed(ImGuiKey_A)) {
|
||||||
auto const &img = m_model.activeSubSheet();
|
auto const &img = m_model.activeSubSheet();
|
||||||
m_model.setSelection({{}, {img.columns * TileWidth - 1, img.rows * TileHeight - 1}});
|
m_model.setSelection({
|
||||||
|
{},
|
||||||
|
{img.columns * TileWidth - 1, img.rows * TileHeight - 1}});
|
||||||
} else if (ImGui::IsKeyPressed(ImGuiKey_G)) {
|
} else if (ImGui::IsKeyPressed(ImGuiKey_G)) {
|
||||||
m_model.clearSelection();
|
m_model.clearSelection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
auto const scale = ig::dpiScale();
|
||||||
auto const paneSize = ImGui::GetContentRegionAvail();
|
auto const paneSize = ImGui::GetContentRegionAvail();
|
||||||
auto const tileSheetParentSize = ImVec2{paneSize.x - s_palViewWidth, paneSize.y};
|
auto const tileSheetParentSize = ImVec2{paneSize.x - s_palViewWidth * scale, paneSize.y};
|
||||||
auto const fbSize = ox::Vec2{tileSheetParentSize.x - 16, tileSheetParentSize.y - 16};
|
auto const fbSize = ox::Vec2{tileSheetParentSize.x - 16 * scale, tileSheetParentSize.y - 16 * scale};
|
||||||
ImGui::BeginChild("TileSheetView", tileSheetParentSize, true);
|
ImGui::BeginChild("TileSheetView", tileSheetParentSize, true);
|
||||||
{
|
{
|
||||||
drawTileSheet(fbSize);
|
drawTileSheet(fbSize);
|
||||||
}
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::BeginChild("Controls", {s_palViewWidth - 8, paneSize.y}, true);
|
ImGui::BeginChild("Controls", {(s_palViewWidth - 8) * scale, paneSize.y}, true);
|
||||||
{
|
{
|
||||||
auto const controlsSize = ImGui::GetContentRegionAvail();
|
auto const controlsSize = ImGui::GetContentRegionAvail();
|
||||||
ImGui::BeginChild("ToolBox", {0, 32}, true);
|
ImGui::BeginChild("ToolBox", {0, 32 * scale}, true);
|
||||||
{
|
{
|
||||||
auto const btnSz = ImVec2{45, 14};
|
auto const btnSz = ImVec2{45 * scale, 14 * scale};
|
||||||
if (ImGui::Selectable("Select", m_tool == TileSheetTool::Select, 0, btnSz)) {
|
if (ImGui::Selectable("Select", m_tool == TileSheetTool::Select, 0, btnSz)) {
|
||||||
m_tool = TileSheetTool::Select;
|
m_tool = TileSheetTool::Select;
|
||||||
}
|
}
|
||||||
@@ -193,7 +196,7 @@ void TileSheetEditorImGui::draw(studio::Context&) noexcept {
|
|||||||
//ig::ComboBox("##Operations", ox::Array<ox::CStringView, 1>{"Operations"}, i);
|
//ig::ComboBox("##Operations", ox::Array<ox::CStringView, 1>{"Operations"}, i);
|
||||||
}
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
ImGui::BeginChild("OperationsBox", {0, 35}, ImGuiWindowFlags_NoTitleBar);
|
ImGui::BeginChild("OperationsBox", {0, 35 * scale}, ImGuiWindowFlags_NoTitleBar);
|
||||||
{
|
{
|
||||||
if (ImGui::BeginCombo("##Operations", "Operations", 0)) {
|
if (ImGui::BeginCombo("##Operations", "Operations", 0)) {
|
||||||
if (ImGui::Selectable("Flip X", false)) {
|
if (ImGui::Selectable("Flip X", false)) {
|
||||||
@@ -214,17 +217,17 @@ void TileSheetEditorImGui::draw(studio::Context&) noexcept {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
auto const ySize = controlsSize.y - (38 + ig::BtnSz.y + 21);
|
auto const ySize = controlsSize.y - (38 + ig::BtnSz.y + 21) * scale;
|
||||||
// draw palette/color picker
|
// draw palette/color picker
|
||||||
ImGui::BeginChild("Palette", {s_palViewWidth - 24, ySize / 2.f}, true);
|
ImGui::BeginChild("Palette", {(s_palViewWidth - 24) * scale, ySize / 2.f}, true);
|
||||||
{
|
{
|
||||||
drawPaletteMenu();
|
drawPaletteMenu();
|
||||||
}
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
ImGui::BeginChild("SubSheets", {s_palViewWidth - 24, ySize / 2.f}, true);
|
ImGui::BeginChild("SubSheets", {(s_palViewWidth - 24) * scale, ySize / 2.f}, true);
|
||||||
{
|
{
|
||||||
static constexpr auto btnHeight = ig::BtnSz.y;
|
auto constexpr btnHeight = ig::BtnSz.y;
|
||||||
auto constexpr btnSize = ImVec2{btnHeight, btnHeight};
|
auto const btnSize = ImVec2{btnHeight, btnHeight};
|
||||||
if (ig::PushButton("+", btnSize)) {
|
if (ig::PushButton("+", btnSize)) {
|
||||||
auto insertOnIdx = m_model.activeSubSheetIdx();
|
auto insertOnIdx = m_model.activeSubSheetIdx();
|
||||||
auto const &parent = m_model.activeSubSheet();
|
auto const &parent = m_model.activeSubSheet();
|
||||||
@@ -247,16 +250,16 @@ void TileSheetEditorImGui::draw(studio::Context&) noexcept {
|
|||||||
if (ig::PushButton("Export")) {
|
if (ig::PushButton("Export")) {
|
||||||
m_exportMenu.show();
|
m_exportMenu.show();
|
||||||
}
|
}
|
||||||
TileSheet::SubSheetIdx path;
|
|
||||||
static constexpr auto flags =
|
static constexpr auto flags =
|
||||||
ImGuiTableFlags_RowBg |
|
ImGuiTableFlags_RowBg |
|
||||||
ImGuiTableFlags_NoBordersInBody |
|
ImGuiTableFlags_NoBordersInBody |
|
||||||
ImGuiTableFlags_ScrollY;
|
ImGuiTableFlags_ScrollY;
|
||||||
if (ImGui::BeginTable("Subsheets", 4, flags)) {
|
if (ImGui::BeginTable("Subsheets", 4, flags)) {
|
||||||
|
TileSheet::SubSheetIdx path;
|
||||||
ImGui::TableSetupColumn("Subsheet", ImGuiTableColumnFlags_NoHide);
|
ImGui::TableSetupColumn("Subsheet", ImGuiTableColumnFlags_NoHide);
|
||||||
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 25);
|
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 25 * scale);
|
||||||
ImGui::TableSetupColumn("Columns", ImGuiTableColumnFlags_WidthFixed, 50);
|
ImGui::TableSetupColumn("Columns", ImGuiTableColumnFlags_WidthFixed, 50 * scale);
|
||||||
ImGui::TableSetupColumn("Rows", ImGuiTableColumnFlags_WidthFixed, 50);
|
ImGui::TableSetupColumn("Rows", ImGuiTableColumnFlags_WidthFixed, 50 * scale);
|
||||||
ImGui::TableHeadersRow();
|
ImGui::TableHeadersRow();
|
||||||
drawSubsheetSelector(m_view.img().subsheet, path);
|
drawSubsheetSelector(m_view.img().subsheet, path);
|
||||||
ImGui::EndTable();
|
ImGui::EndTable();
|
||||||
@@ -265,8 +268,8 @@ void TileSheetEditorImGui::draw(studio::Context&) noexcept {
|
|||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
}
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
m_subsheetEditor.draw(m_tctx);
|
m_subsheetEditor.draw();
|
||||||
m_exportMenu.draw(m_tctx);
|
m_exportMenu.draw();
|
||||||
if (auto pal = m_palPicker.draw(m_sctx)) {
|
if (auto pal = m_palPicker.draw(m_sctx)) {
|
||||||
if (*pal != m_model.palPath()) {
|
if (*pal != m_model.palPath()) {
|
||||||
oxLogError(m_model.setPalette(*pal));
|
oxLogError(m_model.setPalette(*pal));
|
||||||
@@ -445,10 +448,11 @@ void TileSheetEditorImGui::drawTileSheet(ox::Vec2 const &fbSize) noexcept {
|
|||||||
|
|
||||||
void TileSheetEditorImGui::drawPaletteMenu() noexcept {
|
void TileSheetEditorImGui::drawPaletteMenu() noexcept {
|
||||||
ig::IDStackItem const idStackItem{"PaletteMenu"};
|
ig::IDStackItem const idStackItem{"PaletteMenu"};
|
||||||
auto constexpr comboWidthSub = 62;
|
auto const scale = ig::dpiScale();
|
||||||
|
auto const comboWidthSub = 62 * scale;
|
||||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - comboWidthSub);
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - comboWidthSub);
|
||||||
auto constexpr palTags = ImGuiInputTextFlags_ReadOnly;
|
auto constexpr palFlags = ImGuiInputTextFlags_ReadOnly;
|
||||||
if (ig::InputTextWithHint("##Palette", "Path to Palette", m_model.palPath(), palTags)) {
|
if (ig::InputTextWithHint("##Palette", "Path to Palette", m_model.palPath(), palFlags)) {
|
||||||
oxLogError(m_model.setPalette(m_model.palPath()));
|
oxLogError(m_model.setPalette(m_model.palPath()));
|
||||||
}
|
}
|
||||||
m_palPathFocused = ImGui::IsItemFocused();
|
m_palPathFocused = ImGui::IsItemFocused();
|
||||||
@@ -556,7 +560,7 @@ void TileSheetEditorImGui::SubSheetEditor::show(ox::StringViewCR name, int const
|
|||||||
m_rows = rows;
|
m_rows = rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TileSheetEditorImGui::SubSheetEditor::draw(turbine::Context &tctx) noexcept {
|
void TileSheetEditorImGui::SubSheetEditor::draw() noexcept {
|
||||||
constexpr auto popupName = "Edit Subsheet";
|
constexpr auto popupName = "Edit Subsheet";
|
||||||
if (!m_show) {
|
if (!m_show) {
|
||||||
return;
|
return;
|
||||||
@@ -565,13 +569,13 @@ void TileSheetEditorImGui::SubSheetEditor::draw(turbine::Context &tctx) noexcept
|
|||||||
auto constexpr popupWidth = 235.f;
|
auto constexpr popupWidth = 235.f;
|
||||||
auto const popupHeight = modSize ? 130.f : 85.f;
|
auto const popupHeight = modSize ? 130.f : 85.f;
|
||||||
auto const popupSz = ImVec2{popupWidth, popupHeight};
|
auto const popupSz = ImVec2{popupWidth, popupHeight};
|
||||||
if (ig::BeginPopup(tctx, popupName, m_show, popupSz)) {
|
if (ig::BeginPopup(popupName, m_show, popupSz)) {
|
||||||
ig::InputText("Name", m_name);
|
ig::InputText("Name", m_name);
|
||||||
if (modSize) {
|
if (modSize) {
|
||||||
ImGui::InputInt("Columns", &m_cols);
|
ImGui::InputInt("Columns", &m_cols);
|
||||||
ImGui::InputInt("Rows", &m_rows);
|
ImGui::InputInt("Rows", &m_rows);
|
||||||
}
|
}
|
||||||
if (ig::PopupControlsOkCancel(popupWidth, m_show) == ig::PopupResponse::OK) {
|
if (ig::PopupControlsOkCancel(m_show) == ig::PopupResponse::OK) {
|
||||||
inputSubmitted.emit(m_name, m_cols, m_rows);
|
inputSubmitted.emit(m_name, m_cols, m_rows);
|
||||||
}
|
}
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
@@ -588,18 +592,18 @@ void TileSheetEditorImGui::ExportMenu::show() noexcept {
|
|||||||
m_scale = 5;
|
m_scale = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TileSheetEditorImGui::ExportMenu::draw(turbine::Context &tctx) noexcept {
|
void TileSheetEditorImGui::ExportMenu::draw() noexcept {
|
||||||
constexpr auto popupName = "Export Tile Sheet";
|
constexpr auto popupName = "Export Tile Sheet";
|
||||||
if (!m_show) {
|
if (!m_show) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
constexpr auto popupWidth = 235.f;
|
auto constexpr popupWidth = 235.f;
|
||||||
constexpr auto popupHeight = 85.f;
|
auto constexpr popupHeight = 85.f;
|
||||||
constexpr auto popupSz = ImVec2{popupWidth, popupHeight};
|
auto constexpr popupSz = ImVec2{popupWidth, popupHeight};
|
||||||
if (ig::BeginPopup(tctx, popupName, m_show, popupSz)) {
|
if (ig::BeginPopup(popupName, m_show, popupSz)) {
|
||||||
ImGui::InputInt("Scale", &m_scale);
|
ImGui::InputInt("Scale", &m_scale);
|
||||||
m_scale = ox::clamp(m_scale, 1, 135);
|
m_scale = ox::clamp(m_scale, 1, 135);
|
||||||
if (ig::PopupControlsOkCancel(popupWidth, m_show) == ig::PopupResponse::OK) {
|
if (ig::PopupControlsOkCancel(m_show) == ig::PopupResponse::OK) {
|
||||||
inputSubmitted.emit(m_scale);
|
inputSubmitted.emit(m_scale);
|
||||||
}
|
}
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class TileSheetEditorImGui: public studio::Editor {
|
|||||||
public:
|
public:
|
||||||
ox::Signal<ox::Error(ox::StringViewCR name, int cols, int rows)> inputSubmitted;
|
ox::Signal<ox::Error(ox::StringViewCR name, int cols, int rows)> inputSubmitted;
|
||||||
void show(ox::StringViewCR name, int cols, int rows) noexcept;
|
void show(ox::StringViewCR name, int cols, int rows) noexcept;
|
||||||
void draw(turbine::Context &tctx) noexcept;
|
void draw() noexcept;
|
||||||
void close() noexcept;
|
void close() noexcept;
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
constexpr bool isOpen() const noexcept { return m_show; }
|
constexpr bool isOpen() const noexcept { return m_show; }
|
||||||
@@ -39,7 +39,7 @@ class TileSheetEditorImGui: public studio::Editor {
|
|||||||
public:
|
public:
|
||||||
ox::Signal<ox::Error(int scale)> inputSubmitted;
|
ox::Signal<ox::Error(int scale)> inputSubmitted;
|
||||||
void show() noexcept;
|
void show() noexcept;
|
||||||
void draw(turbine::Context &tctx) noexcept;
|
void draw() noexcept;
|
||||||
void close() noexcept;
|
void close() noexcept;
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
constexpr bool isOpen() const noexcept { return m_show; }
|
constexpr bool isOpen() const noexcept { return m_show; }
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ target_link_libraries(
|
|||||||
|
|
||||||
target_compile_definitions(
|
target_compile_definitions(
|
||||||
NostalgiaStudio PUBLIC
|
NostalgiaStudio PUBLIC
|
||||||
OLYMPIC_APP_VERSION="dev build"
|
OLYMPIC_APP_VERSION="d2026.05.0"
|
||||||
|
OLYMPIC_APP_ID="net.drinkingtea.nostalgia.NostalgiaStudio"
|
||||||
)
|
)
|
||||||
|
|
||||||
install(
|
install(
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
|
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>dev build</string>
|
<string>d2026.05.0</string>
|
||||||
|
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
<string>12.0.0</string>
|
<string>12.0.0</string>
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
add_executable(nostalgia-pack)
|
|
||||||
|
|
||||||
target_link_libraries(
|
|
||||||
nostalgia-pack
|
|
||||||
KeelPack-AppLib
|
|
||||||
NostalgiaKeelModules
|
|
||||||
NostalgiaProfile
|
|
||||||
OlympicApplib
|
|
||||||
)
|
|
||||||
|
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Release" AND NOT WIN32)
|
|
||||||
# enable LTO
|
|
||||||
set_property(TARGET nostalgia-pack PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
install(
|
|
||||||
TARGETS
|
|
||||||
nostalgia-pack
|
|
||||||
RUNTIME DESTINATION
|
|
||||||
bin
|
|
||||||
)
|
|
||||||
@@ -9,6 +9,10 @@
|
|||||||
#define OLYMPIC_PROJECT_NAME "OlympicProject"
|
#define OLYMPIC_PROJECT_NAME "OlympicProject"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef OLYMPIC_APP_ID
|
||||||
|
#define OLYMPIC_APP_ID "AppID"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef OLYMPIC_APP_NAME
|
#ifndef OLYMPIC_APP_NAME
|
||||||
#define OLYMPIC_APP_NAME "App"
|
#define OLYMPIC_APP_NAME "App"
|
||||||
#endif
|
#endif
|
||||||
@@ -38,7 +42,9 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace olympic {
|
namespace olympic {
|
||||||
ox::String appVersion{OLYMPIC_APP_VERSION};
|
ox::StringLiteral appVersion{OLYMPIC_APP_VERSION};
|
||||||
|
ox::StringLiteral appId{OLYMPIC_APP_ID};
|
||||||
|
ox::StringLiteral projectName{OLYMPIC_PROJECT_NAME};
|
||||||
}
|
}
|
||||||
|
|
||||||
ox::Error run(
|
ox::Error run(
|
||||||
@@ -72,7 +78,7 @@ int main(int const argc, char const **argv) {
|
|||||||
OLYMPIC_PROJECT_DATADIR,
|
OLYMPIC_PROJECT_DATADIR,
|
||||||
{argv, static_cast<size_t>(argc)});
|
{argv, static_cast<size_t>(argc)});
|
||||||
oxAssert(err, "Something went wrong...");
|
oxAssert(err, "Something went wrong...");
|
||||||
if (err) {
|
if (ox::defines::Debug && err) {
|
||||||
oxErrf("Failure: {}\n", toStr(err));
|
oxErrf("Failure: {}\n", toStr(err));
|
||||||
}
|
}
|
||||||
return static_cast<int>(err);
|
return static_cast<int>(err);
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ox/std/error.hpp>
|
||||||
|
#include <ox/std/span.hpp>
|
||||||
|
#include <ox/std/stringview.hpp>
|
||||||
|
|
||||||
|
namespace keel {
|
||||||
|
|
||||||
|
ox::Error pack(
|
||||||
|
ox::StringViewCR projectDir,
|
||||||
|
ox::StringViewCR projectDataDir,
|
||||||
|
ox::SpanView<ox::CString> argv) noexcept;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -41,20 +41,15 @@ install(
|
|||||||
|
|
||||||
if(TURBINE_BUILD_TYPE STREQUAL "Native")
|
if(TURBINE_BUILD_TYPE STREQUAL "Native")
|
||||||
add_library(
|
add_library(
|
||||||
KeelPack-AppLib
|
KeelPack-PackLib
|
||||||
pack-applib.cpp
|
pack-packlib.cpp
|
||||||
)
|
)
|
||||||
target_include_directories(
|
target_include_directories(
|
||||||
KeelPack-AppLib PUBLIC
|
KeelPack-PackLib PUBLIC
|
||||||
../include
|
../include
|
||||||
)
|
)
|
||||||
target_compile_definitions(
|
|
||||||
KeelPack-AppLib PUBLIC
|
|
||||||
OLYMPIC_LOAD_KEEL_MODULES=1
|
|
||||||
OLYMPIC_APP_NAME="Keel Pack"
|
|
||||||
)
|
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
KeelPack-AppLib
|
KeelPack-PackLib
|
||||||
Keel
|
Keel
|
||||||
OxClArgs
|
OxClArgs
|
||||||
OxClaw
|
OxClaw
|
||||||
@@ -62,7 +57,7 @@ if(TURBINE_BUILD_TYPE STREQUAL "Native")
|
|||||||
)
|
)
|
||||||
install(
|
install(
|
||||||
TARGETS
|
TARGETS
|
||||||
KeelPack-AppLib
|
KeelPack-PackLib
|
||||||
DESTINATION
|
DESTINATION
|
||||||
LIBRARY DESTINATION lib
|
LIBRARY DESTINATION lib
|
||||||
ARCHIVE DESTINATION lib
|
ARCHIVE DESTINATION lib
|
||||||
|
|||||||
@@ -92,22 +92,20 @@ static ox::Error pack(
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ox::Error run(
|
namespace keel {
|
||||||
[[maybe_unused]] ox::StringView const project,
|
|
||||||
[[maybe_unused]] ox::StringView const appName,
|
ox::Error pack(
|
||||||
ox::StringView const projectDataDir,
|
ox::StringViewCR projectDir,
|
||||||
|
ox::StringViewCR projectDataDir,
|
||||||
ox::SpanView<ox::CString> const argv) noexcept {
|
ox::SpanView<ox::CString> const argv) noexcept {
|
||||||
ox::ClArgs const args(argv);
|
ox::ClArgs const args(argv);
|
||||||
auto const argSrc = args.getString("src", "");
|
|
||||||
auto const argRomBin = args.getString("rom-bin", "");
|
auto const argRomBin = args.getString("rom-bin", "");
|
||||||
auto const argManifest = args.getString("manifest", "");
|
auto const argManifest = args.getString("manifest", "");
|
||||||
if (argSrc == "") {
|
|
||||||
oxErr("\033[31;1;1merror:\033[0m must specify a source directory\n");
|
|
||||||
return ox::Error(1, "must specify a source directory");
|
|
||||||
}
|
|
||||||
if (argRomBin == "") {
|
if (argRomBin == "") {
|
||||||
oxErr("\033[31;1;1merror:\033[0m must specify a path for ROM file\n");
|
oxErr("\033[31;1;1merror:\033[0m must specify a path for ROM file\n");
|
||||||
return ox::Error(1, "must specify a path for preload file");
|
return ox::Error(1, "must specify a path for preload file");
|
||||||
}
|
}
|
||||||
return pack(argSrc, argRomBin, argManifest, projectDataDir);
|
return ::pack(projectDir, argRomBin, argManifest, projectDataDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -15,10 +15,16 @@
|
|||||||
#include <studioapp/studioapp.hpp>
|
#include <studioapp/studioapp.hpp>
|
||||||
|
|
||||||
#include "subcommands/change-format/change-format.hpp"
|
#include "subcommands/change-format/change-format.hpp"
|
||||||
|
#include "subcommands/pack/pack.hpp"
|
||||||
|
|
||||||
#include "configfile.hpp"
|
#include "configfile.hpp"
|
||||||
|
#include "font.hpp"
|
||||||
#include "studioui.hpp"
|
#include "studioui.hpp"
|
||||||
|
|
||||||
|
namespace olympic {
|
||||||
|
extern ox::StringLiteral appId;
|
||||||
|
}
|
||||||
|
|
||||||
namespace studio {
|
namespace studio {
|
||||||
|
|
||||||
static ox::Error convertStudioConfigV1ToStudioConfigV2(
|
static ox::Error convertStudioConfigV1ToStudioConfigV2(
|
||||||
@@ -41,10 +47,8 @@ static struct: Module {
|
|||||||
|
|
||||||
ox::Vector<Command> commands() const final {
|
ox::Vector<Command> commands() const final {
|
||||||
return {
|
return {
|
||||||
{
|
{ "change-format", cmdChangeFormat, },
|
||||||
"change-format",
|
{ "pack", cmdPack, },
|
||||||
cmdChangeFormat,
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} constexpr mod;
|
} constexpr mod;
|
||||||
@@ -69,6 +73,10 @@ class StudioUIDrawer: public turbine::gl::Drawer {
|
|||||||
explicit StudioUIDrawer(StudioUI &ui) noexcept: m_ui(ui) {
|
explicit StudioUIDrawer(StudioUI &ui) noexcept: m_ui(ui) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void preDraw(turbine::Context&) noexcept final {
|
||||||
|
m_ui.preDraw();
|
||||||
|
}
|
||||||
|
|
||||||
void draw(turbine::Context&) noexcept final {
|
void draw(turbine::Context&) noexcept final {
|
||||||
m_ui.draw();
|
m_ui.draw();
|
||||||
}
|
}
|
||||||
@@ -91,7 +99,9 @@ static ox::Error runStudio(
|
|||||||
ox::StringViewCR appName,
|
ox::StringViewCR appName,
|
||||||
ox::StringViewCR projectDataDir,
|
ox::StringViewCR projectDataDir,
|
||||||
ox::UPtr<ox::FileSystem> &&fs) noexcept {
|
ox::UPtr<ox::FileSystem> &&fs) noexcept {
|
||||||
OX_REQUIRE_M(ctx, turbine::init(std::move(fs), appName));
|
OX_REQUIRE_M(
|
||||||
|
ctx,
|
||||||
|
turbine::init(std::move(fs), appName, olympic::appId));
|
||||||
oxLogError(turbine::setWindowIcon(*ctx, WindowIcons()));
|
oxLogError(turbine::setWindowIcon(*ctx, WindowIcons()));
|
||||||
turbine::setWindowTitle(*ctx, keelCtx(*ctx).appName);
|
turbine::setWindowTitle(*ctx, keelCtx(*ctx).appName);
|
||||||
turbine::setKeyEventHandler(*ctx, keyEventHandler);
|
turbine::setKeyEventHandler(*ctx, keyEventHandler);
|
||||||
@@ -105,6 +115,20 @@ static ox::Error runStudio(
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void listCmds() noexcept {
|
||||||
|
oxOut("Insufficient arguments for sub-command\n\nValid commands:\n\n");
|
||||||
|
for (auto const m : modules()) {
|
||||||
|
if (m->commands().empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
oxOutf("\t{}\n", m->id());
|
||||||
|
for (auto const &c : m->commands()) {
|
||||||
|
oxOutf("\t\t{}\n", c.name);
|
||||||
|
}
|
||||||
|
oxOut("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static ox::Error run(
|
static ox::Error run(
|
||||||
ox::StringViewCR appName,
|
ox::StringViewCR appName,
|
||||||
ox::StringViewCR projectDataDir,
|
ox::StringViewCR projectDataDir,
|
||||||
@@ -119,7 +143,8 @@ static ox::Error run(
|
|||||||
});
|
});
|
||||||
if (args.size() > 1 && ox::StringView{args[1]} == "cmd") {
|
if (args.size() > 1 && ox::StringView{args[1]} == "cmd") {
|
||||||
if (args.size() < 5) {
|
if (args.size() < 5) {
|
||||||
return ox::Error{1, "insufficient arguments for sub-command"};
|
listCmds();
|
||||||
|
return ox::Error{1};
|
||||||
}
|
}
|
||||||
auto constexpr numCmdArgs = 5;
|
auto constexpr numCmdArgs = 5;
|
||||||
ox::StringView const projectDir = args[2];
|
ox::StringView const projectDir = args[2];
|
||||||
@@ -129,13 +154,10 @@ static ox::Error run(
|
|||||||
if (m->id() == moduleId) {
|
if (m->id() == moduleId) {
|
||||||
for (auto const &c : m->commands()) {
|
for (auto const &c : m->commands()) {
|
||||||
if (c.name == subCmd) {
|
if (c.name == subCmd) {
|
||||||
auto kctx = keel::init(
|
OX_REQUIRE(kctx, keel::init(
|
||||||
ox::make_unique<ox::PassThroughFS>(projectDir),
|
ox::make_unique<ox::PassThroughFS>(projectDir),
|
||||||
c.name);
|
c.name).reoriginate(2, "failed to load project directory"));
|
||||||
if (kctx.error) {
|
Project project{*kctx, projectDir, projectDataDir};
|
||||||
return ox::Error{2, "failed to load project directory"};
|
|
||||||
}
|
|
||||||
Project project{*kctx.value, projectDir, projectDataDir};
|
|
||||||
return c.func(
|
return c.func(
|
||||||
project,
|
project,
|
||||||
args.size() > numCmdArgs ?
|
args.size() > numCmdArgs ?
|
||||||
|
|||||||
@@ -4,8 +4,6 @@
|
|||||||
|
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
|
|
||||||
#include <studio/context.hpp>
|
|
||||||
|
|
||||||
#include "clawviewer.hpp"
|
#include "clawviewer.hpp"
|
||||||
|
|
||||||
namespace studio {
|
namespace studio {
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
// Generated
|
// Generated
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
#include <ox/std/span.hpp>
|
#include <ox/std/span.hpp>
|
||||||
|
|
||||||
namespace studio::files {
|
namespace studio::files {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
#include "about.hpp"
|
#include "about.hpp"
|
||||||
|
|
||||||
namespace olympic {
|
namespace olympic {
|
||||||
extern ox::String appVersion;
|
extern ox::StringLiteral appVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace studio {
|
namespace studio {
|
||||||
@@ -22,7 +22,7 @@ AboutPopup::AboutPopup(turbine::Context &ctx) noexcept:
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void AboutPopup::draw(Context &sctx) noexcept {
|
void AboutPopup::draw(Context&) noexcept {
|
||||||
if (ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
if (ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||||
close();
|
close();
|
||||||
return;
|
return;
|
||||||
@@ -37,12 +37,12 @@ void AboutPopup::draw(Context &sctx) noexcept {
|
|||||||
case Stage::Open: {
|
case Stage::Open: {
|
||||||
constexpr auto modalFlags =
|
constexpr auto modalFlags =
|
||||||
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
|
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
|
||||||
ig::centerNextWindow(sctx.tctx);
|
ig::centerNextWindow();
|
||||||
auto open = true;
|
auto open = true;
|
||||||
if (ImGui::BeginPopupModal("About", &open, modalFlags)) {
|
if (ImGui::BeginPopupModal("About", &open, modalFlags)) {
|
||||||
ImGui::Text("%s\n\nBuild date: %s", m_text.c_str(), __DATE__);
|
ImGui::Text("%s\n\nBuild date: %s", m_text.c_str(), __DATE__);
|
||||||
ImGui::NewLine();
|
ImGui::NewLine();
|
||||||
ImGui::Dummy({148.0f, 0.0f});
|
ig::Dummy({148.0f, 0.0f});
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ig::PushButton("Close")) {
|
if (ig::PushButton("Close")) {
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ ox::Error FileInfo::open(ox::StringParam filePath) noexcept {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileInfo::draw(Context &sctx) noexcept {
|
void FileInfo::draw(Context&) noexcept {
|
||||||
if (ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
if (ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||||
close();
|
close();
|
||||||
return;
|
return;
|
||||||
@@ -52,7 +52,7 @@ void FileInfo::draw(Context &sctx) noexcept {
|
|||||||
case Stage::Open: {
|
case Stage::Open: {
|
||||||
constexpr auto modalFlags =
|
constexpr auto modalFlags =
|
||||||
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
|
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
|
||||||
ig::centerNextWindow(sctx.tctx);
|
ig::centerNextWindow();
|
||||||
auto open = true;
|
auto open = true;
|
||||||
if (ImGui::BeginPopupModal(m_title.c_str(), &open, modalFlags)) {
|
if (ImGui::BeginPopupModal(m_title.c_str(), &open, modalFlags)) {
|
||||||
drawTable();
|
drawTable();
|
||||||
@@ -79,13 +79,14 @@ void FileInfo::draw(Context &sctx) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void FileInfo::drawTable() const noexcept {
|
void FileInfo::drawTable() const noexcept {
|
||||||
|
auto const scale = ig::dpiScale();
|
||||||
ig::IDStackItem const idStackItem{"FileInfo"};
|
ig::IDStackItem const idStackItem{"FileInfo"};
|
||||||
ImGui::Text("%s", m_filePath.c_str());
|
ImGui::Text("%s", m_filePath.c_str());
|
||||||
if (m_fileInfo && ImGui::BeginTable(
|
if (m_fileInfo && ImGui::BeginTable(
|
||||||
"Table", 2,
|
"Table", 2,
|
||||||
ImGuiTableFlags_Borders |
|
ImGuiTableFlags_Borders |
|
||||||
ImGuiTableFlags_RowBg)) {
|
ImGuiTableFlags_RowBg)) {
|
||||||
ImGui::TableSetupColumn("Field", ImGuiTableColumnFlags_WidthFixed, 70);
|
ImGui::TableSetupColumn("Field", ImGuiTableColumnFlags_WidthFixed, 70 * scale);
|
||||||
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_NoHide);
|
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_NoHide);
|
||||||
// asset id
|
// asset id
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
|
|||||||
@@ -37,9 +37,10 @@ void MakeCopyPopup::draw(Context &ctx) noexcept {
|
|||||||
m_stage = Stage::Open;
|
m_stage = Stage::Open;
|
||||||
m_open = true;
|
m_open = true;
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
case Stage::Open:
|
case Stage::Open: {
|
||||||
ig::centerNextWindow(ctx.tctx);
|
auto const scale = ig::dpiScale();
|
||||||
ImGui::SetNextWindowSize({250, 0});
|
ig::centerNextWindow();
|
||||||
|
ImGui::SetNextWindowSize({250 * scale, 0});
|
||||||
constexpr auto modalFlags =
|
constexpr auto modalFlags =
|
||||||
ImGuiWindowFlags_NoCollapse |
|
ImGuiWindowFlags_NoCollapse |
|
||||||
ImGuiWindowFlags_NoMove |
|
ImGuiWindowFlags_NoMove |
|
||||||
@@ -67,6 +68,7 @@ void MakeCopyPopup::draw(Context &ctx) noexcept {
|
|||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -134,9 +134,10 @@ void NewMenu::drawNewItemPath(Context &sctx) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void NewMenu::drawButtons(Stage const next) noexcept {
|
void NewMenu::drawButtons(Stage const next) noexcept {
|
||||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 198);
|
auto const scale = ig::dpiScale();
|
||||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - 20);
|
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 198 * scale);
|
||||||
constexpr ImVec2 btnSz{60, 20};
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - 22 * scale);
|
||||||
|
ImVec2 const btnSz{60 * scale, ig::BtnSz.y * scale};
|
||||||
if (ImGui::Button("Back", btnSz)) {
|
if (ImGui::Button("Back", btnSz)) {
|
||||||
if (auto const p = m_prev.back(); p.ok()) {
|
if (auto const p = m_prev.back(); p.ok()) {
|
||||||
m_stage = *p.value;
|
m_stage = *p.value;
|
||||||
@@ -156,9 +157,10 @@ void NewMenu::drawButtons(Stage const next) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void NewMenu::drawFirstPageButtons(Stage const next) noexcept {
|
void NewMenu::drawFirstPageButtons(Stage const next) noexcept {
|
||||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 130);
|
auto const scale = ig::dpiScale();
|
||||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - 20);
|
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 130 * scale);
|
||||||
constexpr ImVec2 btnSz{60, 20};
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - 22 * scale);
|
||||||
|
ImVec2 const btnSz{60 * scale, ig::BtnSz.y * scale};
|
||||||
if (ImGui::Button("Next", btnSz)) {
|
if (ImGui::Button("Next", btnSz)) {
|
||||||
m_prev.emplace_back(m_stage);
|
m_prev.emplace_back(m_stage);
|
||||||
m_stage = next;
|
m_stage = next;
|
||||||
@@ -171,9 +173,10 @@ void NewMenu::drawFirstPageButtons(Stage const next) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void NewMenu::drawLastPageButtons(Context &sctx) noexcept {
|
void NewMenu::drawLastPageButtons(Context &sctx) noexcept {
|
||||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 198);
|
auto const scale = ig::dpiScale();
|
||||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - 20);
|
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 198 * scale);
|
||||||
constexpr ImVec2 btnSz{60, 20};
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - 22 * scale);
|
||||||
|
ImVec2 const btnSz{60 * scale, ig::BtnSz.y * scale};
|
||||||
if (ImGui::Button("Back", btnSz)) {
|
if (ImGui::Button("Back", btnSz)) {
|
||||||
if (auto const p = m_prev.back(); p.ok()) {
|
if (auto const p = m_prev.back(); p.ok()) {
|
||||||
m_stage = *p.value;
|
m_stage = *p.value;
|
||||||
|
|||||||
@@ -65,8 +65,9 @@ void NewProject::drawNewProjectName(Context &sctx) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void NewProject::drawLastPageButtons(Context&) noexcept {
|
void NewProject::drawLastPageButtons(Context&) noexcept {
|
||||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 110);
|
auto const scale = ig::dpiScale();
|
||||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - 22);
|
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 110 * scale);
|
||||||
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - 22 * scale);
|
||||||
if (ig::PushButton("Finish")) {
|
if (ig::PushButton("Finish")) {
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ ox::Vector<Module const*> const &modules() noexcept {
|
|||||||
return g_modules;
|
return g_modules;
|
||||||
}
|
}
|
||||||
|
|
||||||
void registerModule(Module const*mod) noexcept {
|
void registerModule(Module const *mod) noexcept {
|
||||||
if (mod) {
|
if (mod) {
|
||||||
g_modules.emplace_back(mod);
|
g_modules.emplace_back(mod);
|
||||||
}
|
}
|
||||||
@@ -121,19 +121,6 @@ using StudioConfig = StudioConfigV2;
|
|||||||
StudioUI::StudioUI(turbine::Context &tctx, ox::StringParam projectDataDir) noexcept:
|
StudioUI::StudioUI(turbine::Context &tctx, ox::StringParam projectDataDir) noexcept:
|
||||||
m_sctx{*this, tctx},
|
m_sctx{*this, tctx},
|
||||||
m_projectDataDir{std::move(projectDataDir)} {
|
m_projectDataDir{std::move(projectDataDir)} {
|
||||||
{
|
|
||||||
ImFontConfig fontCfg;
|
|
||||||
fontCfg.FontDataOwnedByAtlas = false;
|
|
||||||
auto const &io = ImGui::GetIO();
|
|
||||||
auto const font = files::RobotoMedium_ttf();
|
|
||||||
// const_cast is needed because this data is definitely const,
|
|
||||||
// but AddFontFromMemoryTTF requires a mutable buffer.
|
|
||||||
// However, setting fontCfg.FontDataOwnedByAtlas ensures
|
|
||||||
// that it will still be treated as const.
|
|
||||||
// ImGui documentation recognizes that this is a bad design,
|
|
||||||
// and hopefully it will change at some point.
|
|
||||||
io.Fonts->AddFontFromMemoryTTF(const_cast<uint8_t*>(font.data()), static_cast<int>(font.size()), 13, &fontCfg);
|
|
||||||
}
|
|
||||||
auto &kctx = keelCtx(m_tctx);
|
auto &kctx = keelCtx(m_tctx);
|
||||||
kctx.converters.emplace_back(keel::Converter::make<convertStudioConfigV1ToStudioConfigV2>());
|
kctx.converters.emplace_back(keel::Converter::make<convertStudioConfigV1ToStudioConfigV2>());
|
||||||
oxLogError(headerizeConfigFile<StudioConfigV1>(kctx));
|
oxLogError(headerizeConfigFile<StudioConfigV1>(kctx));
|
||||||
@@ -196,7 +183,16 @@ void StudioUI::handleNavigationChange(ox::StringParam path, ox::StringParam navA
|
|||||||
m_navAction.emplace(std::move(path), std::move(navArgs));
|
m_navAction.emplace(std::move(path), std::move(navArgs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StudioUI::preDraw() noexcept {
|
||||||
|
loadFont();
|
||||||
|
}
|
||||||
|
|
||||||
void StudioUI::draw() noexcept {
|
void StudioUI::draw() noexcept {
|
||||||
|
if (turbine::isWayland() || ox::defines::OS == ox::OS::Darwin) {
|
||||||
|
ig::setDpiScale(1.f);
|
||||||
|
} else {
|
||||||
|
ig::setDpiScale(ImGui::GetWindowDpiScale());
|
||||||
|
}
|
||||||
glutils::clearScreen();
|
glutils::clearScreen();
|
||||||
drawMenu();
|
drawMenu();
|
||||||
auto const &viewport = *ImGui::GetMainViewport();
|
auto const &viewport = *ImGui::GetMainViewport();
|
||||||
@@ -215,7 +211,7 @@ void StudioUI::draw() noexcept {
|
|||||||
ImGuiFocusedFlags_RootAndChildWindows | ImGuiFocusedFlags_NoPopupHierarchy);
|
ImGuiFocusedFlags_RootAndChildWindows | ImGuiFocusedFlags_NoPopupHierarchy);
|
||||||
if (m_showProjectExplorer) {
|
if (m_showProjectExplorer) {
|
||||||
auto const v = ImGui::GetContentRegionAvail();
|
auto const v = ImGui::GetContentRegionAvail();
|
||||||
m_projectExplorer.draw(m_sctx, {300, v.y});
|
m_projectExplorer.draw(m_sctx, {300 * ig::dpiScale(), v.y});
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
}
|
}
|
||||||
drawTabBar();
|
drawTabBar();
|
||||||
@@ -229,6 +225,29 @@ void StudioUI::draw() noexcept {
|
|||||||
procFileMoves();
|
procFileMoves();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StudioUI::loadFont() noexcept {
|
||||||
|
auto const scale = turbine::isWayland() || ox::defines::OS == ox::OS::Darwin ?
|
||||||
|
1.f : turbine::scale(m_tctx);
|
||||||
|
if (m_fontScale != scale) {
|
||||||
|
m_fontScale = scale;
|
||||||
|
ImFontConfig fontCfg;
|
||||||
|
fontCfg.FontDataOwnedByAtlas = false;
|
||||||
|
auto const &io = ImGui::GetIO();
|
||||||
|
auto const font = files::RobotoMedium_ttf();
|
||||||
|
// const_cast is needed because this data is definitely const,
|
||||||
|
// but AddFontFromMemoryTTF requires a mutable buffer.
|
||||||
|
// However, setting fontCfg.FontDataOwnedByAtlas ensures
|
||||||
|
// that it will still be treated as const.
|
||||||
|
// ImGui documentation recognizes that this is a bad design,
|
||||||
|
// and hopefully it will change at some point.
|
||||||
|
io.Fonts->AddFontFromMemoryTTF(
|
||||||
|
const_cast<uint8_t*>(font.data()),
|
||||||
|
static_cast<int>(font.size()),
|
||||||
|
13 * scale,
|
||||||
|
&fontCfg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool StudioUI::handleShutdown() noexcept {
|
bool StudioUI::handleShutdown() noexcept {
|
||||||
auto const out = ox::all_of(m_editors.begin(), m_editors.end(), [](ox::UPtr<BaseEditor> const &e) {
|
auto const out = ox::all_of(m_editors.begin(), m_editors.end(), [](ox::UPtr<BaseEditor> const &e) {
|
||||||
return !e->unsavedChanges();
|
return !e->unsavedChanges();
|
||||||
@@ -322,12 +341,14 @@ void StudioUI::drawMenu() noexcept {
|
|||||||
if (ImGui::BeginMenu("Navigate")) {
|
if (ImGui::BeginMenu("Navigate")) {
|
||||||
constexpr auto backShortcut = ox::defines::OS == ox::OS::Darwin ? "Cmd+[" : "Alt+Left Arrow";
|
constexpr auto backShortcut = ox::defines::OS == ox::OS::Darwin ? "Cmd+[" : "Alt+Left Arrow";
|
||||||
constexpr auto fwdShortcut = ox::defines::OS == ox::OS::Darwin ? "Cmd+]" : "Alt+Right Arrow";
|
constexpr auto fwdShortcut = ox::defines::OS == ox::OS::Darwin ? "Cmd+]" : "Alt+Right Arrow";
|
||||||
if (ImGui::MenuItem("Back", backShortcut, false, m_sctx.navIdx > 1)) {
|
if (ImGui::MenuItem(
|
||||||
|
"Back", backShortcut, false,
|
||||||
|
m_sctx.navIdx && *m_sctx.navIdx > 1)) {
|
||||||
navigateBack(m_sctx);
|
navigateBack(m_sctx);
|
||||||
}
|
}
|
||||||
if (ImGui::MenuItem(
|
if (ImGui::MenuItem(
|
||||||
"Forward", fwdShortcut, false,
|
"Forward", fwdShortcut, false,
|
||||||
m_sctx.navIdx < m_sctx.navStack.size())) {
|
m_sctx.navIdx && *m_sctx.navIdx < m_sctx.navStack.size())) {
|
||||||
navigateForward(m_sctx);
|
navigateForward(m_sctx);
|
||||||
}
|
}
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
@@ -358,7 +379,7 @@ void StudioUI::drawTabs() noexcept {
|
|||||||
auto const &e = *it;
|
auto const &e = *it;
|
||||||
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 | ImGuiTabItemFlags_NoAssumedClosure;
|
||||||
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]] {
|
||||||
@@ -376,11 +397,17 @@ void StudioUI::drawTabs() noexcept {
|
|||||||
if (m_activeEditorOnLastDraw != e.get()) [[unlikely]] {
|
if (m_activeEditorOnLastDraw != e.get()) [[unlikely]] {
|
||||||
m_activeEditor->onActivated();
|
m_activeEditor->onActivated();
|
||||||
if (!m_sctx.navIdx ||
|
if (!m_sctx.navIdx ||
|
||||||
(m_sctx.navIdx <= m_sctx.navStack.size() &&
|
*m_sctx.navIdx == m_sctx.navStack.size() ||
|
||||||
m_sctx.navStack[m_sctx.navIdx - 1].filePath != m_activeEditor->itemPath())) {
|
(*m_sctx.navIdx < m_sctx.navStack.size() &&
|
||||||
m_sctx.navStack.resize(m_sctx.navIdx);
|
m_sctx.navStack[*m_sctx.navIdx].filePath != m_activeEditor->itemPath())) {
|
||||||
++m_sctx.navIdx;
|
auto constexpr defaultVal =
|
||||||
m_sctx.navStack.emplace_back(ox::String{m_activeEditor->itemPath()}, ox::String{""});
|
ox::MaxValue<ox::remove_reference_t<decltype(*m_sctx.navIdx)>>;
|
||||||
|
auto &navIdx = m_sctx.navIdx.emplace(m_sctx.navIdx.or_value(defaultVal));
|
||||||
|
++navIdx;
|
||||||
|
m_sctx.navStack.resize(navIdx);
|
||||||
|
m_sctx.navStack.emplace_back(
|
||||||
|
ox::String{m_activeEditor->itemPath()},
|
||||||
|
ox::String{});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (m_closeActiveTab) [[unlikely]] {
|
if (m_closeActiveTab) [[unlikely]] {
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ class StudioUI final: public ox::SignalHandler {
|
|||||||
ox::String args;
|
ox::String args;
|
||||||
};
|
};
|
||||||
ox::Optional<NavAction> m_navAction;
|
ox::Optional<NavAction> m_navAction;
|
||||||
|
float m_fontScale{};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit StudioUI(turbine::Context &tctx, ox::StringParam projectDataDir) noexcept;
|
explicit StudioUI(turbine::Context &tctx, ox::StringParam projectDataDir) noexcept;
|
||||||
@@ -102,9 +103,13 @@ class StudioUI final: public ox::SignalHandler {
|
|||||||
bool handleShutdown() noexcept;
|
bool handleShutdown() noexcept;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void preDraw() noexcept;
|
||||||
|
|
||||||
void draw() noexcept;
|
void draw() noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void loadFont() noexcept;
|
||||||
|
|
||||||
void drawMenu() noexcept;
|
void drawMenu() noexcept;
|
||||||
|
|
||||||
void drawTabBar() noexcept;
|
void drawTabBar() noexcept;
|
||||||
|
|||||||
@@ -2,9 +2,11 @@
|
|||||||
target_sources(
|
target_sources(
|
||||||
StudioAppLib PRIVATE
|
StudioAppLib PRIVATE
|
||||||
change-format/change-format.cpp
|
change-format/change-format.cpp
|
||||||
|
pack/pack.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
StudioAppLib PUBLIC
|
StudioAppLib PUBLIC
|
||||||
OxClArgs
|
OxClArgs
|
||||||
|
KeelPack-PackLib
|
||||||
)
|
)
|
||||||
@@ -20,13 +20,12 @@ static ox::Error convertFile(
|
|||||||
OX_REQUIRE(uuid, keel::readUuidHeader(buff));
|
OX_REQUIRE(uuid, keel::readUuidHeader(buff));
|
||||||
OX_REQUIRE(obj, keel::readAsset(ts, buff).reoriginate(1, "unable to parse file"));
|
OX_REQUIRE(obj, keel::readAsset(ts, buff).reoriginate(1, "unable to parse file"));
|
||||||
buff.clear();
|
buff.clear();
|
||||||
ox::BufferWriter wrtr{&buff};
|
ox::BufferWriter writer{&buff};
|
||||||
OX_RETURN_ERROR(keel::writeUuidHeader(wrtr, uuid));
|
OX_RETURN_ERROR(keel::writeUuidHeader(writer, uuid));
|
||||||
OX_RETURN_ERROR(ox::writeClaw(obj, wrtr, fmt));
|
OX_RETURN_ERROR(ox::writeClaw(obj, writer, fmt));
|
||||||
if (fmt == ox::ClawFormat::Organic) {
|
if (fmt == ox::ClawFormat::Organic) {
|
||||||
*buff.back().value = '\n';
|
*buff.back().value = '\n';
|
||||||
}
|
}
|
||||||
OX_RETURN_ERROR(wrtr.write(buff.data(), buff.size()));
|
|
||||||
OX_RETURN_ERROR(fs.write(path, buff).reoriginate(1, "unable to write file"));
|
OX_RETURN_ERROR(fs.write(path, buff).reoriginate(1, "unable to write file"));
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -37,9 +36,9 @@ static void printUsage() noexcept {
|
|||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static constexpr ox::Result<ox::ClawFormat> getFmt(ox::StringViewCR fmtStr) noexcept {
|
static constexpr ox::Result<ox::ClawFormat> getFmt(ox::StringViewCR fmtStr) noexcept {
|
||||||
if (caseInsensitiveEquals(fmtStr, "mc") == 0) {
|
if (caseInsensitiveStrCmp(fmtStr, "mc") == 0) {
|
||||||
return ox::ClawFormat::Metal;
|
return ox::ClawFormat::Metal;
|
||||||
} else if (caseInsensitiveEquals(fmtStr, "oc") == 0) {
|
} else if (caseInsensitiveStrCmp(fmtStr, "oc") == 0) {
|
||||||
return ox::ClawFormat::Organic;
|
return ox::ClawFormat::Organic;
|
||||||
}
|
}
|
||||||
return ox::Error{1, "invalid format"};
|
return ox::Error{1, "invalid format"};
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <studio/project.hpp>
|
||||||
|
|
||||||
namespace studio {
|
namespace studio {
|
||||||
|
|
||||||
ox::Error cmdChangeFormat(Project &project, ox::SpanView<ox::CString> args) noexcept;
|
ox::Error cmdChangeFormat(Project &project, ox::SpanView<ox::CString> args) noexcept;
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <keel/pack-packlib.hpp>
|
||||||
|
|
||||||
|
#include "pack.hpp"
|
||||||
|
|
||||||
|
namespace olympic {
|
||||||
|
extern ox::StringLiteral projectName;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace studio {
|
||||||
|
|
||||||
|
ox::Error cmdPack(Project &project, ox::SpanView<ox::CString> args) noexcept {
|
||||||
|
return keel::pack(project.projectPath(), olympic::projectName, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <studio/project.hpp>
|
||||||
|
|
||||||
|
namespace studio {
|
||||||
|
|
||||||
|
ox::Error cmdPack(Project &project, ox::SpanView<ox::CString> args) noexcept;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -28,7 +28,7 @@ struct Context {
|
|||||||
ox::String filePath;
|
ox::String filePath;
|
||||||
ox::String navArgs;
|
ox::String navArgs;
|
||||||
};
|
};
|
||||||
size_t navIdx{};
|
ox::Optional<size_t> navIdx{};
|
||||||
ox::Vector<NavPath> navStack;
|
ox::Vector<NavPath> navStack;
|
||||||
std::function<void(ox::StringParam filePath, ox::StringParam navArgs)> navCallback;
|
std::function<void(ox::StringParam filePath, ox::StringParam navArgs)> navCallback;
|
||||||
Context(StudioUI &pUi, turbine::Context &pTctx) noexcept:
|
Context(StudioUI &pUi, turbine::Context &pTctx) noexcept:
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ namespace studio::ig {
|
|||||||
|
|
||||||
inline constexpr auto BtnSz = ImVec2{52, 22};
|
inline constexpr auto BtnSz = ImVec2{52, 22};
|
||||||
|
|
||||||
|
void setDpiScale(float scale) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
float dpiScale() noexcept;
|
||||||
|
|
||||||
constexpr ImTextureID toImTextureID(ox::Unsigned_c auto id) noexcept
|
constexpr ImTextureID toImTextureID(ox::Unsigned_c auto id) noexcept
|
||||||
requires(sizeof(id) <= sizeof(ox::Uint<sizeof(ImTextureID)*8>)) {
|
requires(sizeof(id) <= sizeof(ox::Uint<sizeof(ImTextureID)*8>)) {
|
||||||
@@ -133,7 +137,7 @@ auto dragDropTarget(auto const &cb) noexcept {
|
|||||||
|
|
||||||
class ChildStackItem {
|
class ChildStackItem {
|
||||||
public:
|
public:
|
||||||
explicit ChildStackItem(ox::CStringViewCR id, ImVec2 const &sz = {}) noexcept;
|
explicit ChildStackItem(ox::CStringViewCR id, ImVec2 sz = {}) noexcept;
|
||||||
~ChildStackItem() noexcept;
|
~ChildStackItem() noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -153,7 +157,12 @@ class IndentStackItem {
|
|||||||
~IndentStackItem() noexcept;
|
~IndentStackItem() noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
void centerNextWindow(turbine::Context &ctx) noexcept;
|
void centerNextWindow() noexcept;
|
||||||
|
|
||||||
|
inline void Dummy(ImVec2 const &sz) noexcept {
|
||||||
|
auto const scale = dpiScale();
|
||||||
|
ImGui::Dummy({sz.x * scale, sz.y * scale});
|
||||||
|
}
|
||||||
|
|
||||||
bool PushButton(ox::CStringViewCR lbl, ImVec2 const &btnSz = BtnSz) noexcept;
|
bool PushButton(ox::CStringViewCR lbl, ImVec2 const &btnSz = BtnSz) noexcept;
|
||||||
|
|
||||||
@@ -173,10 +182,10 @@ TextInput<ox::IString<MaxChars>> InputText(
|
|||||||
ox::StringViewCR currentText,
|
ox::StringViewCR currentText,
|
||||||
ImGuiInputTextFlags const flags = 0,
|
ImGuiInputTextFlags const flags = 0,
|
||||||
ImGuiInputTextCallback const callback = nullptr,
|
ImGuiInputTextCallback const callback = nullptr,
|
||||||
void *user_data = nullptr) noexcept {
|
void *const userData = nullptr) noexcept {
|
||||||
TextInput<ox::IString<MaxChars>> out = {.text = currentText};
|
TextInput<ox::IString<MaxChars>> out = {.text = currentText};
|
||||||
out.changed = ImGui::InputText(
|
out.changed = ImGui::InputText(
|
||||||
label.c_str(), out.text.data(), MaxChars + 1, flags, callback, user_data);
|
label.c_str(), out.text.data(), MaxChars + 1, flags, callback, userData);
|
||||||
if (out.changed) {
|
if (out.changed) {
|
||||||
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
||||||
std::ignore = out.text.unsafeResize(ox::strlen(out.text.c_str()));
|
std::ignore = out.text.unsafeResize(ox::strlen(out.text.c_str()));
|
||||||
@@ -192,10 +201,10 @@ TextInput<ox::IString<MaxChars>> InputTextWithHint(
|
|||||||
ox::StringViewCR currentText,
|
ox::StringViewCR currentText,
|
||||||
ImGuiInputTextFlags const flags = 0,
|
ImGuiInputTextFlags const flags = 0,
|
||||||
ImGuiInputTextCallback const callback = nullptr,
|
ImGuiInputTextCallback const callback = nullptr,
|
||||||
void *user_data = nullptr) noexcept {
|
void *userData = nullptr) noexcept {
|
||||||
TextInput<ox::IString<MaxChars>> out = {.text = currentText};
|
TextInput<ox::IString<MaxChars>> out = {.text = currentText};
|
||||||
out.changed = ImGui::InputTextWithHint(
|
out.changed = ImGui::InputTextWithHint(
|
||||||
label.c_str(), hint.c_str(), out.text.data(), MaxChars + 1, flags, callback, user_data);
|
label.c_str(), hint.c_str(), out.text.data(), MaxChars + 1, flags, callback, userData);
|
||||||
if (out.changed) {
|
if (out.changed) {
|
||||||
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
||||||
std::ignore = out.text.unsafeResize(ox::strlen(out.text.c_str()));
|
std::ignore = out.text.unsafeResize(ox::strlen(out.text.c_str()));
|
||||||
@@ -210,9 +219,9 @@ bool InputText(
|
|||||||
ox::IString<StrCap> &text,
|
ox::IString<StrCap> &text,
|
||||||
ImGuiInputTextFlags const flags = 0,
|
ImGuiInputTextFlags const flags = 0,
|
||||||
ImGuiInputTextCallback const callback = nullptr,
|
ImGuiInputTextCallback const callback = nullptr,
|
||||||
void *user_data = nullptr) noexcept {
|
void *userData = nullptr) noexcept {
|
||||||
auto const out = ImGui::InputText(
|
auto const out = ImGui::InputText(
|
||||||
label.c_str(), text.data(), StrCap + 1, flags, callback, user_data);
|
label.c_str(), text.data(), StrCap + 1, flags, callback, userData);
|
||||||
if (out) {
|
if (out) {
|
||||||
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
||||||
std::ignore = text.unsafeResize(ox::strlen(text.c_str()));
|
std::ignore = text.unsafeResize(ox::strlen(text.c_str()));
|
||||||
@@ -243,7 +252,7 @@ PopupResponse PopupControlsOk(
|
|||||||
ox::CStringViewCR ok);
|
ox::CStringViewCR ok);
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
bool BeginPopup(turbine::Context &ctx, ox::CStringViewCR popupName, bool &show, ImVec2 const &sz = {285, 0});
|
bool BeginPopup(ox::CStringViewCR popupName, bool &show, ImVec2 sz = {285, 0});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -290,7 +299,7 @@ bool ListBox(
|
|||||||
std::function<ox::CStringView(size_t)> const &f,
|
std::function<ox::CStringView(size_t)> const &f,
|
||||||
size_t strCnt,
|
size_t strCnt,
|
||||||
size_t &selIdx,
|
size_t &selIdx,
|
||||||
ImVec2 const &sz = {0, 0}) noexcept;
|
ImVec2 sz = {0, 0}) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -15,12 +15,9 @@ void navigateTo(Context &ctx, ox::StringParam filePath, ox::StringParam navArgs)
|
|||||||
}
|
}
|
||||||
path = p;
|
path = p;
|
||||||
}
|
}
|
||||||
//if (
|
auto constexpr defaultVal =
|
||||||
// auto const [np, err] = ctx.navStack.back();
|
ox::MaxValue<ox::remove_reference_t<decltype(*ctx.navIdx)>>;
|
||||||
// !err && np->filePath == path && np->navArgs == navArgs.view()) {
|
ctx.navStack.resize(ctx.navIdx.or_value(defaultVal) + 1);
|
||||||
// return;
|
|
||||||
//}
|
|
||||||
ctx.navStack.resize(ctx.navIdx + 1);
|
|
||||||
ctx.navStack.emplace_back(ox::String{path}, ox::String{navArgs.view()});
|
ctx.navStack.emplace_back(ox::String{path}, ox::String{navArgs.view()});
|
||||||
try {
|
try {
|
||||||
ctx.navCallback(std::move(path), std::move(navArgs));
|
ctx.navCallback(std::move(path), std::move(navArgs));
|
||||||
@@ -34,15 +31,20 @@ void navigateBack(Context &ctx) noexcept {
|
|||||||
if (!ctx.navIdx) {
|
if (!ctx.navIdx) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
--ctx.navIdx;
|
auto &navIdx = *ctx.navIdx;
|
||||||
while (ctx.navIdx < ctx.navStack.size() && ctx.navIdx) {
|
while (navIdx && navIdx < ctx.navStack.size()) {
|
||||||
auto const i = ctx.navIdx - 1;
|
--navIdx;
|
||||||
auto const &n = ctx.navStack[i];
|
auto const &n = ctx.navStack[navIdx];
|
||||||
|
// check for invalid entry
|
||||||
if (!ctx.project->exists(n.filePath)) {
|
if (!ctx.project->exists(n.filePath)) {
|
||||||
std::ignore = ctx.navStack.erase(i);
|
// remove the invalid entry, then reset and retry
|
||||||
--ctx.navIdx;
|
auto const err = ctx.navStack.erase(navIdx);
|
||||||
|
if (err.error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// entry is valid, do the navigation
|
||||||
try {
|
try {
|
||||||
ctx.navCallback(n.filePath, n.navArgs);
|
ctx.navCallback(n.filePath, n.navArgs);
|
||||||
} catch (std::exception const &e) {
|
} catch (std::exception const &e) {
|
||||||
@@ -54,20 +56,24 @@ void navigateBack(Context &ctx) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void navigateForward(Context &ctx) noexcept {
|
void navigateForward(Context &ctx) noexcept {
|
||||||
auto const nextIdx = ctx.navIdx + 1;
|
if (!ctx.navIdx) {
|
||||||
|
ctx.navIdx.emplace(0u);
|
||||||
|
}
|
||||||
|
auto &navIdx = *ctx.navIdx;
|
||||||
|
auto const nextIdx = navIdx + 1;
|
||||||
while (nextIdx < ctx.navStack.size()) {
|
while (nextIdx < ctx.navStack.size()) {
|
||||||
auto const &n = ctx.navStack[nextIdx];
|
auto const &n = ctx.navStack[nextIdx];
|
||||||
|
if (!ctx.project->exists(n.filePath)) {
|
||||||
|
std::ignore = ctx.navStack.erase(nextIdx);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
ctx.navCallback(n.filePath, n.navArgs);
|
ctx.navCallback(n.filePath, n.navArgs);
|
||||||
} catch (std::exception const &e) {
|
} catch (std::exception const &e) {
|
||||||
oxAssert(ctx.navCallback != nullptr, "navCallback is null");
|
oxAssert(ctx.navCallback != nullptr, "navCallback is null");
|
||||||
oxErrf("navigateForward failed: {}", e.what());
|
oxErrf("navigateForward failed: {}", e.what());
|
||||||
}
|
}
|
||||||
if (!ctx.project->exists(n.filePath)) {
|
++navIdx;
|
||||||
std::ignore = ctx.navStack.erase(nextIdx);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
++ctx.navIdx;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <turbine/turbine.hpp>
|
||||||
|
|
||||||
#include <studio/imguiutil.hpp>
|
#include <studio/imguiutil.hpp>
|
||||||
|
|
||||||
#include <studio/filepickerpopup.hpp>
|
#include <studio/filepickerpopup.hpp>
|
||||||
@@ -83,9 +85,10 @@ ox::Optional<ox::String> FilePickerPopup::draw(Context &ctx) noexcept {
|
|||||||
if (!m_open) {
|
if (!m_open) {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
if (ig::BeginPopup(ctx.tctx, m_name, m_open, {380, 340})) {
|
auto const scale = turbine::scale(ctx.tctx);
|
||||||
|
if (ig::BeginPopup(m_name, m_open, {380, 340})) {
|
||||||
auto const vp = ImGui::GetContentRegionAvail();
|
auto const vp = ImGui::GetContentRegionAvail();
|
||||||
m_explorer.draw(ctx, {vp.x, vp.y - 30});
|
m_explorer.draw(ctx, {vp.x, vp.y - 30 * scale});
|
||||||
if (ig::PopupControlsOkCancel(m_open) == ig::PopupResponse::OK || m_explorer.opened) {
|
if (ig::PopupControlsOkCancel(m_open) == ig::PopupResponse::OK || m_explorer.opened) {
|
||||||
out = handlePick();
|
out = handlePick();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,25 @@
|
|||||||
|
|
||||||
namespace studio::ig {
|
namespace studio::ig {
|
||||||
|
|
||||||
ChildStackItem::ChildStackItem(ox::CStringViewCR id, ImVec2 const &sz) noexcept {
|
inline void scaleSz(ImVec2 &sz) noexcept {
|
||||||
|
auto const scale = dpiScale();
|
||||||
|
sz.x *= scale;
|
||||||
|
sz.y *= scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
static thread_local float g_dpiScale = 1.f;
|
||||||
|
|
||||||
|
void setDpiScale(float const scale) noexcept {
|
||||||
|
g_dpiScale = scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
float dpiScale() noexcept {
|
||||||
|
return g_dpiScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChildStackItem::ChildStackItem(ox::CStringViewCR id, ImVec2 sz) noexcept {
|
||||||
|
scaleSz(sz);
|
||||||
ImGui::BeginChild(id.c_str(), sz);
|
ImGui::BeginChild(id.c_str(), sz);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +51,7 @@ IDStackItem::~IDStackItem() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
IndentStackItem::IndentStackItem(float indent) noexcept: m_indent(indent) {
|
IndentStackItem::IndentStackItem(float id) noexcept: m_indent(id) {
|
||||||
ImGui::Indent(m_indent);
|
ImGui::Indent(m_indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,16 +60,18 @@ IndentStackItem::~IndentStackItem() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void centerNextWindow(turbine::Context &ctx) noexcept {
|
void centerNextWindow() noexcept {
|
||||||
auto const sz = turbine::getScreenSize(ctx);
|
auto const &io = ImGui::GetIO();
|
||||||
auto const screenW = static_cast<float>(sz.width);
|
auto const sz = io.DisplaySize;
|
||||||
auto const screenH = static_cast<float>(sz.height);
|
ImGui::SetNextWindowPos(
|
||||||
auto const mod = ImGui::GetWindowDpiScale() * 2;
|
{sz.x * 0.5f, sz.y * 0.5f},
|
||||||
ImGui::SetNextWindowPos(ImVec2(screenW / mod, screenH / mod), ImGuiCond_Always, ImVec2(0.5f, 0.5f));
|
ImGuiCond_Always,
|
||||||
|
{0.5f, 0.5f});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PushButton(ox::CStringViewCR lbl, ImVec2 const &btnSz) noexcept {
|
bool PushButton(ox::CStringViewCR lbl, ImVec2 const &btnSz) noexcept {
|
||||||
return ImGui::Button(lbl.c_str(), btnSz);
|
auto const scale = dpiScale();
|
||||||
|
return ImGui::Button(lbl.c_str(), {btnSz.x * scale, btnSz.y * scale});
|
||||||
}
|
}
|
||||||
|
|
||||||
PopupResponse PopupControlsOkCancel(
|
PopupResponse PopupControlsOkCancel(
|
||||||
@@ -60,9 +80,10 @@ PopupResponse PopupControlsOkCancel(
|
|||||||
ox::CStringViewCR ok,
|
ox::CStringViewCR ok,
|
||||||
ox::CStringViewCR cancel) {
|
ox::CStringViewCR cancel) {
|
||||||
auto out = PopupResponse::None;
|
auto out = PopupResponse::None;
|
||||||
constexpr auto btnSz = ImVec2{50, BtnSz.y};
|
auto const scale = dpiScale();
|
||||||
|
auto const btnSz = ImVec2{50 * scale, BtnSz.y * scale};
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::SetCursorPosX(popupWidth - 118);
|
ImGui::SetCursorPosX(popupWidth - 118 * scale);
|
||||||
if (ImGui::Button(ok.c_str(), btnSz)) {
|
if (ImGui::Button(ok.c_str(), btnSz)) {
|
||||||
popupOpen = false;
|
popupOpen = false;
|
||||||
out = PopupResponse::OK;
|
out = PopupResponse::OK;
|
||||||
@@ -79,16 +100,18 @@ PopupResponse PopupControlsOkCancel(
|
|||||||
bool &popupOpen,
|
bool &popupOpen,
|
||||||
ox::CStringViewCR ok,
|
ox::CStringViewCR ok,
|
||||||
ox::CStringViewCR cancel) {
|
ox::CStringViewCR cancel) {
|
||||||
return PopupControlsOkCancel(ImGui::GetContentRegionAvail().x + 17, popupOpen, ok, cancel);
|
auto const scale = dpiScale();
|
||||||
|
return PopupControlsOkCancel(ImGui::GetContentRegionAvail().x + 17 * scale, popupOpen, ok, cancel);
|
||||||
}
|
}
|
||||||
|
|
||||||
PopupResponse PopupControlsOk(
|
PopupResponse PopupControlsOk(
|
||||||
bool &popupOpen,
|
bool &popupOpen,
|
||||||
ox::CStringViewCR ok) {
|
ox::CStringViewCR ok) {
|
||||||
|
auto const scale = dpiScale();
|
||||||
auto out = PopupResponse::None;
|
auto out = PopupResponse::None;
|
||||||
constexpr auto btnSz = ImVec2{50, BtnSz.y};
|
auto const btnSz = ImVec2{50 * scale, BtnSz.y * scale};
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::SetCursorPosX(ImGui::GetContentRegionAvail().x - 42);
|
ImGui::SetCursorPosX(ImGui::GetContentRegionAvail().x - 42 * scale);
|
||||||
if (ImGui::Button(ok.c_str(), btnSz)) {
|
if (ImGui::Button(ok.c_str(), btnSz)) {
|
||||||
popupOpen = false;
|
popupOpen = false;
|
||||||
out = PopupResponse::OK;
|
out = PopupResponse::OK;
|
||||||
@@ -101,23 +124,24 @@ PopupResponse PopupControlsOk(
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BeginPopup(turbine::Context &ctx, ox::CStringViewCR popupName, bool &show, ImVec2 const &sz) {
|
bool BeginPopup(ox::CStringViewCR popupName, bool &show, ImVec2 sz) {
|
||||||
|
scaleSz(sz);
|
||||||
constexpr auto modalFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
|
constexpr auto modalFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
|
||||||
centerNextWindow(ctx);
|
centerNextWindow();
|
||||||
ImGui::OpenPopup(popupName.c_str());
|
ImGui::OpenPopup(popupName.c_str());
|
||||||
ImGui::SetNextWindowSize(sz);
|
ImGui::SetNextWindowSize(sz);
|
||||||
return ImGui::BeginPopupModal(popupName.c_str(), &show, modalFlags);
|
return ImGui::BeginPopupModal(popupName.c_str(), &show, modalFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ComboBox(
|
bool ComboBox(
|
||||||
ox::CStringView const &lbl,
|
ox::CStringViewCR lbl,
|
||||||
ox::SpanView<ox::CStringView> const list,
|
ox::SpanView<ox::CStringView> const list,
|
||||||
size_t &selectedIdx) noexcept {
|
size_t &selectedIdx) noexcept {
|
||||||
bool out{};
|
bool out{};
|
||||||
auto const first = selectedIdx < list.size() ? list[selectedIdx].c_str() : "";
|
auto const first = selectedIdx < list.size() ? list[selectedIdx].c_str() : "";
|
||||||
if (ImGui::BeginCombo(lbl.c_str(), first, 0)) {
|
if (ImGui::BeginCombo(lbl.c_str(), first, 0)) {
|
||||||
for (auto i = 0u; i < list.size(); ++i) {
|
for (auto i = 0u; i < list.size(); ++i) {
|
||||||
auto const selected = (selectedIdx == i);
|
auto const selected = selectedIdx == i;
|
||||||
if (ImGui::Selectable(list[i].c_str(), selected) && selectedIdx != i) {
|
if (ImGui::Selectable(list[i].c_str(), selected) && selectedIdx != i) {
|
||||||
selectedIdx = i;
|
selectedIdx = i;
|
||||||
out = true;
|
out = true;
|
||||||
@@ -129,14 +153,14 @@ bool ComboBox(
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ComboBox(
|
bool ComboBox(
|
||||||
ox::CStringView const &lbl,
|
ox::CStringViewCR lbl,
|
||||||
ox::Span<ox::String const> const list,
|
ox::Span<ox::String const> const list,
|
||||||
size_t &selectedIdx) noexcept {
|
size_t &selectedIdx) noexcept {
|
||||||
bool out{};
|
bool out{};
|
||||||
auto const first = selectedIdx < list.size() ? list[selectedIdx].c_str() : "";
|
auto const first = selectedIdx < list.size() ? list[selectedIdx].c_str() : "";
|
||||||
if (ImGui::BeginCombo(lbl.c_str(), first, 0)) {
|
if (ImGui::BeginCombo(lbl.c_str(), first, 0)) {
|
||||||
for (auto i = 0u; i < list.size(); ++i) {
|
for (auto i = 0u; i < list.size(); ++i) {
|
||||||
auto const selected = (selectedIdx == i);
|
auto const selected = selectedIdx == i;
|
||||||
if (ImGui::Selectable(list[i].c_str(), selected) && selectedIdx != i) {
|
if (ImGui::Selectable(list[i].c_str(), selected) && selectedIdx != i) {
|
||||||
selectedIdx = i;
|
selectedIdx = i;
|
||||||
out = true;
|
out = true;
|
||||||
@@ -181,12 +205,14 @@ bool ListBox(
|
|||||||
std::function<ox::CStringView(size_t)> const &f,
|
std::function<ox::CStringView(size_t)> const &f,
|
||||||
size_t const strCnt,
|
size_t const strCnt,
|
||||||
size_t &selIdx,
|
size_t &selIdx,
|
||||||
ImVec2 const &sz) noexcept {
|
ImVec2 sz) noexcept {
|
||||||
|
auto const scale = dpiScale();
|
||||||
|
sz = {sz.x * scale, sz.y * scale};
|
||||||
auto out = false;
|
auto out = false;
|
||||||
if (ImGui::BeginListBox(name.c_str(), sz)) {
|
if (ImGui::BeginListBox(name.c_str(), sz)) {
|
||||||
for (size_t i = 0; i < strCnt; ++i) {
|
for (size_t i = 0; i < strCnt; ++i) {
|
||||||
auto str = f(i);
|
auto const str = f(i);
|
||||||
ig::IDStackItem const idStackItem2(static_cast<int>(i));
|
IDStackItem const idStackItem2(static_cast<int>(i));
|
||||||
if (ImGui::Selectable(str.c_str(), selIdx == i)) {
|
if (ImGui::Selectable(str.c_str(), selIdx == i)) {
|
||||||
if (i != selIdx) {
|
if (i != selIdx) {
|
||||||
selIdx = i;
|
selIdx = i;
|
||||||
@@ -200,13 +226,13 @@ bool ListBox(
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ListBox(ox::CStringViewCR name, ox::SpanView<ox::String> const &list, size_t &selIdx) noexcept {
|
bool ListBox(ox::CStringViewCR name, ox::SpanView<ox::String> const &list, size_t &selIdx) noexcept {
|
||||||
return ListBox(name, [list](size_t i) -> ox::CStringView {
|
return ListBox(name, [list](size_t const i) -> ox::CStringView {
|
||||||
return list[i];
|
return list[i];
|
||||||
}, list.size(), selIdx);
|
}, list.size(), selIdx);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ListBox(ox::CStringViewCR name, ox::SpanView<ox::CStringView> const &list, size_t &selIdx) noexcept {
|
bool ListBox(ox::CStringViewCR name, ox::SpanView<ox::CStringView> const &list, size_t &selIdx) noexcept {
|
||||||
return ListBox(name, [list](size_t i) -> ox::CStringView {
|
return ListBox(name, [list](size_t const i) -> ox::CStringView {
|
||||||
return list[i];
|
return list[i];
|
||||||
}, list.size(), selIdx);
|
}, list.size(), selIdx);
|
||||||
}
|
}
|
||||||
@@ -229,7 +255,7 @@ void FilePicker::draw() noexcept {
|
|||||||
}
|
}
|
||||||
auto constexpr popupSz = ImVec2{450.f, 0};
|
auto constexpr popupSz = ImVec2{450.f, 0};
|
||||||
IDStackItem const idStackItem(m_title);
|
IDStackItem const idStackItem(m_title);
|
||||||
if (BeginPopup(m_sctx.tctx, m_title, m_show, popupSz)) {
|
if (BeginPopup(m_title, m_show, popupSz)) {
|
||||||
auto const &list = m_sctx.project->fileList(m_fileExt);
|
auto const &list = m_sctx.project->fileList(m_fileExt);
|
||||||
size_t selIdx{};
|
size_t selIdx{};
|
||||||
ComboBox(m_title, list, selIdx);
|
ComboBox(m_title, list, selIdx);
|
||||||
@@ -284,7 +310,7 @@ void QuestionPopup::draw(Context &ctx) noexcept {
|
|||||||
turbine::requireRefreshFor(ctx.tctx, 1000);
|
turbine::requireRefreshFor(ctx.tctx, 1000);
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
case Stage::Open:
|
case Stage::Open:
|
||||||
centerNextWindow(ctx.tctx);
|
centerNextWindow();
|
||||||
ImGui::SetNextWindowSize({});
|
ImGui::SetNextWindowSize({});
|
||||||
constexpr auto modalFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
|
constexpr auto modalFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
|
||||||
if (ImGui::BeginPopupModal(m_title.c_str(), &m_open, modalFlags)) {
|
if (ImGui::BeginPopupModal(m_title.c_str(), &m_open, modalFlags)) {
|
||||||
@@ -319,7 +345,7 @@ void MessagePopup::show(ox::StringParam msg) noexcept {
|
|||||||
open();
|
open();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessagePopup::draw(Context &ctx) noexcept {
|
void MessagePopup::draw(Context&) noexcept {
|
||||||
switch (m_stage) {
|
switch (m_stage) {
|
||||||
case Stage::Closed:
|
case Stage::Closed:
|
||||||
break;
|
break;
|
||||||
@@ -329,7 +355,7 @@ void MessagePopup::draw(Context &ctx) noexcept {
|
|||||||
m_open = true;
|
m_open = true;
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
case Stage::Open:
|
case Stage::Open:
|
||||||
centerNextWindow(ctx.tctx);
|
centerNextWindow();
|
||||||
ImGui::SetNextWindowSize({});
|
ImGui::SetNextWindowSize({});
|
||||||
constexpr auto modalFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
|
constexpr auto modalFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
|
||||||
if (ImGui::BeginPopupModal(m_title.c_str(), &m_open, modalFlags)) {
|
if (ImGui::BeginPopupModal(m_title.c_str(), &m_open, modalFlags)) {
|
||||||
|
|||||||
@@ -7,9 +7,9 @@
|
|||||||
|
|
||||||
namespace studio {
|
namespace studio {
|
||||||
|
|
||||||
void Popup::drawWindow(turbine::Context &ctx, bool &open, std::function<void()> const &drawContents) {
|
void Popup::drawWindow(turbine::Context&, bool &open, std::function<void()> const &drawContents) {
|
||||||
ig::centerNextWindow(ctx);
|
ig::centerNextWindow();
|
||||||
ImGui::SetNextWindowSize(static_cast<ImVec2>(m_size));
|
ImGui::SetNextWindowSize(static_cast<ImVec2>(m_size * ig::dpiScale()));
|
||||||
constexpr auto modalFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
|
constexpr auto modalFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
|
||||||
if (ImGui::BeginPopupModal(m_title.c_str(), &open, modalFlags)) {
|
if (ImGui::BeginPopupModal(m_title.c_str(), &open, modalFlags)) {
|
||||||
drawContents();
|
drawContents();
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ namespace gl {
|
|||||||
class Drawer {
|
class Drawer {
|
||||||
public:
|
public:
|
||||||
virtual ~Drawer() = default;
|
virtual ~Drawer() = default;
|
||||||
|
virtual void preDraw(Context&) noexcept {}
|
||||||
virtual void draw(Context&) noexcept = 0;
|
virtual void draw(Context&) noexcept = 0;
|
||||||
};
|
};
|
||||||
void addDrawer(Context &ctx, Drawer *cd) noexcept;
|
void addDrawer(Context &ctx, Drawer *cd) noexcept;
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
#include "gfx.hpp"
|
#include "gfx.hpp"
|
||||||
|
|
||||||
namespace turbine {
|
namespace turbine {
|
||||||
|
|
||||||
class Context;
|
class Context;
|
||||||
|
|
||||||
using TimeMs = uint64_t;
|
using TimeMs = uint64_t;
|
||||||
@@ -91,9 +90,15 @@ KeyEventHandler keyEventHandler(Context const &ctx) noexcept;
|
|||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
bool buttonDown(Context const &ctx, Key) noexcept;
|
bool buttonDown(Context const &ctx, Key) noexcept;
|
||||||
|
|
||||||
ox::Result<ox::UPtr<Context>> init(ox::UPtr<ox::FileSystem> &&fs, ox::StringViewCR appName) noexcept;
|
ox::Result<ox::UPtr<Context>> init(
|
||||||
|
ox::UPtr<ox::FileSystem> &&fs,
|
||||||
|
ox::StringViewCR appName,
|
||||||
|
ox::StringViewCR appId = {}) noexcept;
|
||||||
|
|
||||||
ox::Result<ox::UPtr<Context>> init(ox::StringViewCR fsPath, ox::StringViewCR appName) noexcept;
|
ox::Result<ox::UPtr<Context>> init(
|
||||||
|
ox::StringViewCR fsPath,
|
||||||
|
ox::StringViewCR appName,
|
||||||
|
ox::StringViewCR appId = {}) noexcept;
|
||||||
|
|
||||||
ox::Error run(Context &ctx) noexcept;
|
ox::Error run(Context &ctx) noexcept;
|
||||||
|
|
||||||
@@ -112,4 +117,10 @@ void setShutdownHandler(Context &ctx, ShutdownHandler handler) noexcept;
|
|||||||
// sleep time is a minimum of ~16 milliseconds.
|
// sleep time is a minimum of ~16 milliseconds.
|
||||||
void setUpdateHandler(Context &ctx, UpdateHandler) noexcept;
|
void setUpdateHandler(Context &ctx, UpdateHandler) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
float scale(Context const &ctx) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
bool isWayland() noexcept;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ ox::Error setWindowBounds(Context&, ox::Bounds const&) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ox::Result<ox::UPtr<Context>> init(
|
ox::Result<ox::UPtr<Context>> init(
|
||||||
ox::UPtr<ox::FileSystem> &&fs, ox::StringViewCR appName) noexcept {
|
ox::UPtr<ox::FileSystem> &&fs, ox::StringViewCR appName, ox::StringViewCR) noexcept {
|
||||||
auto ctx = ox::make_unique<Context>();
|
auto ctx = ox::make_unique<Context>();
|
||||||
OX_RETURN_ERROR(keel::init(ctx->keelCtx, std::move(fs), appName));
|
OX_RETURN_ERROR(keel::init(ctx->keelCtx, std::move(fs), appName));
|
||||||
#ifdef OX_BARE_METAL
|
#ifdef OX_BARE_METAL
|
||||||
@@ -139,4 +139,12 @@ KeyEventHandler keyEventHandler(Context const &ctx) noexcept {
|
|||||||
return ctx.keyEventHandler;
|
return ctx.keyEventHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float scale(Context const&) noexcept {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isWayland() noexcept {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ class Context {
|
|||||||
uint64_t draws = 0;
|
uint64_t draws = 0;
|
||||||
bool running{};
|
bool running{};
|
||||||
ShutdownHandler shutdownHandler{};
|
ShutdownHandler shutdownHandler{};
|
||||||
|
float scale{};
|
||||||
|
|
||||||
Context() noexcept = default;
|
Context() noexcept = default;
|
||||||
|
|
||||||
|
|||||||
@@ -235,12 +235,15 @@ void requireRefreshFor(Context &ctx, int ms) noexcept {
|
|||||||
|
|
||||||
static void draw(Context &ctx) noexcept {
|
static void draw(Context &ctx) noexcept {
|
||||||
// draw start
|
// draw start
|
||||||
|
for (auto const d : ctx.drawers) {
|
||||||
|
d->preDraw(ctx);
|
||||||
|
}
|
||||||
#if TURBINE_USE_IMGUI
|
#if TURBINE_USE_IMGUI
|
||||||
ImGui_ImplOpenGL3_NewFrame();
|
ImGui_ImplOpenGL3_NewFrame();
|
||||||
ImGui_ImplGlfw_NewFrame();
|
ImGui_ImplGlfw_NewFrame();
|
||||||
ImGui::NewFrame();
|
ImGui::NewFrame();
|
||||||
#endif
|
#endif
|
||||||
for (auto d : ctx.drawers) {
|
for (auto const d : ctx.drawers) {
|
||||||
d->draw(ctx);
|
d->draw(ctx);
|
||||||
}
|
}
|
||||||
#if TURBINE_USE_IMGUI
|
#if TURBINE_USE_IMGUI
|
||||||
@@ -330,7 +333,9 @@ static void handleGlfwWindowCloseEvent(GLFWwindow *window) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ox::Result<ox::UPtr<Context>> init(
|
ox::Result<ox::UPtr<Context>> init(
|
||||||
ox::UPtr<ox::FileSystem> &&fs, ox::StringViewCR appName) noexcept {
|
ox::UPtr<ox::FileSystem> &&fs,
|
||||||
|
ox::StringViewCR appName,
|
||||||
|
ox::StringViewCR appId) noexcept {
|
||||||
auto ctx = ox::make_unique<Context>();
|
auto ctx = ox::make_unique<Context>();
|
||||||
OX_RETURN_ERROR(keel::init(ctx->keelCtx, std::move(fs), appName));
|
OX_RETURN_ERROR(keel::init(ctx->keelCtx, std::move(fs), appName));
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
@@ -339,12 +344,20 @@ ox::Result<ox::UPtr<Context>> init(
|
|||||||
setMandatoryRefreshPeriod(*ctx, ticksMs(*ctx) + config::MandatoryRefreshPeriod);
|
setMandatoryRefreshPeriod(*ctx, ticksMs(*ctx) + config::MandatoryRefreshPeriod);
|
||||||
// init GLFW context
|
// init GLFW context
|
||||||
glfwInit();
|
glfwInit();
|
||||||
|
glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);
|
||||||
glfwSetErrorCallback(handleGlfwError);
|
glfwSetErrorCallback(handleGlfwError);
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
|
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
|
||||||
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
|
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
|
||||||
|
{
|
||||||
|
auto appIdCstr = ox_malloca(appId.bytes() + 1, char);
|
||||||
|
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
||||||
|
ox::strncpy(appIdCstr.get(), appId.data(), appId.bytes());
|
||||||
|
OX_ALLOW_UNSAFE_BUFFERS_END
|
||||||
|
glfwWindowHintString(GLFW_WAYLAND_APP_ID, appIdCstr.get());
|
||||||
|
}
|
||||||
constexpr auto Scale = 5;
|
constexpr auto Scale = 5;
|
||||||
ctx->window = glfwCreateWindow(
|
ctx->window = glfwCreateWindow(
|
||||||
240 * Scale, 160 * Scale, ctx->keelCtx.appName.c_str(), nullptr, nullptr);
|
240 * Scale, 160 * Scale, ctx->keelCtx.appName.c_str(), nullptr, nullptr);
|
||||||
@@ -373,6 +386,13 @@ ox::Result<ox::UPtr<Context>> init(
|
|||||||
ImGui_ImplOpenGL3_Init();
|
ImGui_ImplOpenGL3_Init();
|
||||||
io.IniFilename = nullptr;
|
io.IniFilename = nullptr;
|
||||||
themeImgui();
|
themeImgui();
|
||||||
|
float xscale{}, yscale{};
|
||||||
|
glfwGetWindowContentScale(ctx->window, &xscale, &yscale);
|
||||||
|
ctx->scale = isWayland() || ox::defines::OS == ox::OS::Darwin ?
|
||||||
|
1.f : ox::max(xscale, yscale);
|
||||||
|
io.DisplayFramebufferScale = ImVec2{ctx->scale, ctx->scale};
|
||||||
|
auto &style = ImGui::GetStyle();
|
||||||
|
style.ScaleAllSizes(ctx->scale);
|
||||||
#endif
|
#endif
|
||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
@@ -471,4 +491,14 @@ KeyEventHandler keyEventHandler(Context const &ctx) noexcept {
|
|||||||
return ctx.keyEventHandler;
|
return ctx.keyEventHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float scale(Context const &ctx) noexcept {
|
||||||
|
float x{}, y{};
|
||||||
|
glfwGetWindowContentScale(ctx.window, &x, &y);
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isWayland() noexcept {
|
||||||
|
return glfwGetPlatform() == GLFW_PLATFORM_WAYLAND;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,12 @@
|
|||||||
|
|
||||||
namespace turbine {
|
namespace turbine {
|
||||||
|
|
||||||
ox::Result<ox::UPtr<Context>> init(ox::StringViewCR fsPath, ox::StringViewCR appName) noexcept {
|
ox::Result<ox::UPtr<Context>> init(
|
||||||
|
ox::StringViewCR fsPath,
|
||||||
|
ox::StringViewCR appName,
|
||||||
|
ox::StringViewCR appId) noexcept {
|
||||||
OX_REQUIRE_M(fs, keel::loadRomFs(fsPath));
|
OX_REQUIRE_M(fs, keel::loadRomFs(fsPath));
|
||||||
return init(std::move(fs), appName);
|
return init(std::move(fs), appName, appId);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -64,7 +64,7 @@ def proc_rsrc_file(rsrc_path: str):# Open and read the JSON file
|
|||||||
pop_ns = ''
|
pop_ns = ''
|
||||||
cpp = '// Generated\n\n#include <ox/std/array.hpp>\n'
|
cpp = '// Generated\n\n#include <ox/std/array.hpp>\n'
|
||||||
cpp += '#include <ox/std/span.hpp>\n\n'
|
cpp += '#include <ox/std/span.hpp>\n\n'
|
||||||
hpp = '// Generated\n\n#include <ox/std/span.hpp>\n\n'
|
hpp = '// Generated\n\n#pragma once\n\n#include <ox/std/span.hpp>\n\n'
|
||||||
cpp += push_ns
|
cpp += push_ns
|
||||||
hpp += push_ns
|
hpp += push_ns
|
||||||
all_files_func_decl = ''
|
all_files_func_decl = ''
|
||||||
|
|||||||
+8
-14
@@ -8,36 +8,30 @@
|
|||||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
#
|
#
|
||||||
|
|
||||||
import platform
|
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
os = platform.system().lower()
|
|
||||||
arch = platform.machine()
|
|
||||||
host_env = f'{os}-{arch}'
|
|
||||||
|
|
||||||
def run(args: list[str]):
|
def run(args: list[str]):
|
||||||
if subprocess.run(args).returncode != 0:
|
if subprocess.run(args).returncode != 0:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# get the current build type
|
|
||||||
with open(".current_build", "r") as f:
|
|
||||||
current_build = f.readlines()[0]
|
|
||||||
if current_build[-1] == '\n':
|
|
||||||
current_build = current_build[:-1]
|
|
||||||
|
|
||||||
project_dir = sys.argv[1]
|
studio_path = sys.argv[1]
|
||||||
project_name = sys.argv[2]
|
project_dir = sys.argv[2]
|
||||||
bin = f'./build/{host_env}-{current_build}/bin/'
|
project_name = sys.argv[3]
|
||||||
project_bin = f'build/gba-release/bin/{project_name}.bin'
|
project_bin = f'build/gba-release/bin/{project_name}.bin'
|
||||||
project_gba = f'{project_name}.gba'
|
project_gba = f'{project_name}.gba'
|
||||||
project_manifest = f'{project_name.lower()}-manifest.json'
|
project_manifest = f'{project_name.lower()}-manifest.json'
|
||||||
|
|
||||||
shutil.copyfile(project_bin, project_gba)
|
shutil.copyfile(project_bin, project_gba)
|
||||||
run([
|
run([
|
||||||
f'{bin}/{project_name.lower()}-pack',
|
studio_path,
|
||||||
'-src', project_dir,
|
'cmd',
|
||||||
|
project_dir,
|
||||||
|
'net.drinkingtea.studio',
|
||||||
|
'pack',
|
||||||
'-rom-bin', project_gba,
|
'-rom-bin', project_gba,
|
||||||
'-manifest', project_manifest])
|
'-manifest', project_manifest])
|
||||||
run(['gbafix', project_gba])
|
run(['gbafix', project_gba])
|
||||||
|
|||||||
Reference in New Issue
Block a user