Compare commits
	
		
			10 Commits
		
	
	
		
			stable
			...
			a18c5d9294
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| a18c5d9294 | |||
| c753881747 | |||
| ba1bf950a8 | |||
| 95950441d1 | |||
| 1b32bdfcad | |||
| 3544392fa8 | |||
| 144d234d09 | |||
| 465fb06f76 | |||
| db953dd0d1 | |||
| daab4dc4f5 | 
							
								
								
									
										8
									
								
								deps/ox/src/ox/std/string.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								deps/ox/src/ox/std/string.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -392,7 +392,7 @@ template<std::size_t SmallStringSize_v> | |||||||
| constexpr BasicString<SmallStringSize_v> BasicString<SmallStringSize_v>::operator+(const char *str) const noexcept { | constexpr BasicString<SmallStringSize_v> BasicString<SmallStringSize_v>::operator+(const char *str) const noexcept { | ||||||
| 	const std::size_t strLen = ox::strlen(str); | 	const std::size_t strLen = ox::strlen(str); | ||||||
| 	const auto currentLen = len(); | 	const auto currentLen = len(); | ||||||
| 	BasicString<SmallStringSize_v> cpy(currentLen + strLen); | 	BasicString<SmallStringSize_v> cpy; | ||||||
| 	cpy.m_buff.resize(m_buff.size() + strLen); | 	cpy.m_buff.resize(m_buff.size() + strLen); | ||||||
| 	ox::listcpy(&cpy.m_buff[0], m_buff.data(), currentLen); | 	ox::listcpy(&cpy.m_buff[0], m_buff.data(), currentLen); | ||||||
| 	ox::listcpy(&cpy.m_buff[currentLen], str, strLen); | 	ox::listcpy(&cpy.m_buff[currentLen], str, strLen); | ||||||
| @@ -425,7 +425,8 @@ constexpr BasicString<SmallStringSize_v> BasicString<SmallStringSize_v>::operato | |||||||
| 	BasicString<SmallStringSize_v> cpy(currentLen + strLen); | 	BasicString<SmallStringSize_v> cpy(currentLen + strLen); | ||||||
| 	cpy.m_buff.resize(m_buff.size() + strLen); | 	cpy.m_buff.resize(m_buff.size() + strLen); | ||||||
| 	ox::listcpy(&cpy.m_buff[0], m_buff.data(), currentLen); | 	ox::listcpy(&cpy.m_buff[0], m_buff.data(), currentLen); | ||||||
| 	ox::listcpy(&cpy.m_buff[currentLen], src.data(), strLen + 1); | 	ox::listcpy(&cpy.m_buff[currentLen], src.data(), strLen); | ||||||
|  | 	cpy.m_buff[cpy.m_buff.size() - 1] = 0; | ||||||
| 	return cpy; | 	return cpy; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -436,7 +437,8 @@ constexpr BasicString<SmallStringSize_v> BasicString<SmallStringSize_v>::operato | |||||||
| 	BasicString<SmallStringSize_v> cpy(currentLen + strLen); | 	BasicString<SmallStringSize_v> cpy(currentLen + strLen); | ||||||
| 	cpy.m_buff.resize(m_buff.size() + strLen); | 	cpy.m_buff.resize(m_buff.size() + strLen); | ||||||
| 	ox::listcpy(&cpy.m_buff[0], m_buff.data(), currentLen); | 	ox::listcpy(&cpy.m_buff[0], m_buff.data(), currentLen); | ||||||
| 	ox::listcpy(&cpy.m_buff[currentLen], src.data(), strLen + 1); | 	ox::listcpy(&cpy.m_buff[currentLen], src.data(), strLen); | ||||||
|  | 	cpy.m_buff[cpy.m_buff.size() - 1] = 0; | ||||||
| 	return cpy; | 	return cpy; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -233,7 +233,9 @@ struct TileSheetV4 { | |||||||
| [[nodiscard]] | [[nodiscard]] | ||||||
| constexpr bool valid(TileSheetV4::SubSheet const&ss, int bpp) noexcept { | constexpr bool valid(TileSheetV4::SubSheet const&ss, int bpp) noexcept { | ||||||
| 	auto const bytes = static_cast<size_t>(ss.columns * ss.rows * PixelsPerTile) / (bpp == 4 ? 2 : 1); | 	auto const bytes = static_cast<size_t>(ss.columns * ss.rows * PixelsPerTile) / (bpp == 4 ? 2 : 1); | ||||||
| 	return ox::all_of(ss.subsheets.begin(), ss.subsheets.end(), | 	return | ||||||
|  | 		(ss.pixels.empty() || ss.subsheets.empty()) && | ||||||
|  | 		ox::all_of(ss.subsheets.begin(), ss.subsheets.end(), | ||||||
| 				[bpp, bytes](TileSheetV4::SubSheet const&s) { | 				[bpp, bytes](TileSheetV4::SubSheet const&s) { | ||||||
| 			return bytes == s.pixels.size() && valid(s, bpp); | 			return bytes == s.pixels.size() && valid(s, bpp); | ||||||
| 		}); | 		}); | ||||||
| @@ -245,8 +247,14 @@ constexpr bool valid(TileSheetV4 const&ts) noexcept { | |||||||
| } | } | ||||||
|  |  | ||||||
| constexpr void repair(TileSheetV4::SubSheet &ss, int bpp) noexcept { | constexpr void repair(TileSheetV4::SubSheet &ss, int bpp) noexcept { | ||||||
|  | 	if (ss.subsheets.empty()) { | ||||||
| 		auto const bytes = static_cast<size_t>(ss.columns * ss.rows * PixelsPerTile) / (bpp == 4 ? 2 : 1); | 		auto const bytes = static_cast<size_t>(ss.columns * ss.rows * PixelsPerTile) / (bpp == 4 ? 2 : 1); | ||||||
| 		ss.pixels.resize(bytes); | 		ss.pixels.resize(bytes); | ||||||
|  | 	} else { | ||||||
|  | 		ss.pixels.clear(); | ||||||
|  | 		ss.columns = -1; | ||||||
|  | 		ss.rows = -1; | ||||||
|  | 	} | ||||||
| 	for (auto &s : ss.subsheets) { | 	for (auto &s : ss.subsheets) { | ||||||
| 		repair(s, bpp); | 		repair(s, bpp); | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| target_sources( | target_sources( | ||||||
| 	NostalgiaCore-Studio PRIVATE | 	NostalgiaCore-Studio PRIVATE | ||||||
| 		commands/addcolorcommand.cpp | 		commands/addcolorcommand.cpp | ||||||
|  | 		commands/addpagecommand.cpp | ||||||
| 		commands/applycolorallpagescommand.cpp | 		commands/applycolorallpagescommand.cpp | ||||||
| 		commands/duplicatepagecommand.cpp | 		commands/duplicatepagecommand.cpp | ||||||
| 		commands/movecolorcommand.cpp | 		commands/movecolorcommand.cpp | ||||||
|   | |||||||
| @@ -0,0 +1,28 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include "commands.hpp" | ||||||
|  |  | ||||||
|  | #include "addpagecommand.hpp" | ||||||
|  |  | ||||||
|  | namespace nostalgia::core { | ||||||
|  |  | ||||||
|  | AddPageCommand::AddPageCommand(Palette &pal) noexcept: | ||||||
|  | 		m_pal(pal) {} | ||||||
|  |  | ||||||
|  | int AddPageCommand::commandId() const noexcept { | ||||||
|  | 	return static_cast<int>(PaletteEditorCommandId::AddPage); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ox::Error AddPageCommand::redo() noexcept { | ||||||
|  | 	m_pal.pages.emplace_back(ox::sfmt("Page {}", m_pal.pages.size() + 1), ox::Vector<PaletteColor>{}); | ||||||
|  | 	return {}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ox::Error AddPageCommand::undo() noexcept { | ||||||
|  | 	m_pal.pages.pop_back(); | ||||||
|  | 	return {}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,31 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <studio/studio.hpp> | ||||||
|  |  | ||||||
|  | #include <nostalgia/core/palette.hpp> | ||||||
|  |  | ||||||
|  | namespace nostalgia::core { | ||||||
|  |  | ||||||
|  | class AddPageCommand: public studio::UndoCommand { | ||||||
|  | 	private: | ||||||
|  | 		Palette &m_pal; | ||||||
|  |  | ||||||
|  | 	public: | ||||||
|  | 		explicit AddPageCommand(Palette &pal) noexcept; | ||||||
|  |  | ||||||
|  | 		~AddPageCommand() noexcept override = default; | ||||||
|  |  | ||||||
|  | 		[[nodiscard]] | ||||||
|  | 		int commandId() const noexcept final; | ||||||
|  |  | ||||||
|  | 		ox::Error redo() noexcept final; | ||||||
|  |  | ||||||
|  | 		ox::Error undo() noexcept final; | ||||||
|  |  | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -9,6 +9,7 @@ namespace nostalgia::core { | |||||||
| enum class PaletteEditorCommandId { | enum class PaletteEditorCommandId { | ||||||
| 	ApplyColorAllPages, | 	ApplyColorAllPages, | ||||||
| 	RenamePage, | 	RenamePage, | ||||||
|  | 	AddPage, | ||||||
| 	DuplicatePage, | 	DuplicatePage, | ||||||
| 	RemovePage, | 	RemovePage, | ||||||
| 	AddColor, | 	AddColor, | ||||||
|   | |||||||
| @@ -23,7 +23,7 @@ int DuplicatePageCommand::commandId() const noexcept { | |||||||
| } | } | ||||||
|  |  | ||||||
| ox::Error DuplicatePageCommand::redo() noexcept { | ox::Error DuplicatePageCommand::redo() noexcept { | ||||||
| 	m_pal.pages.emplace(m_dstIdx, "", std::move(m_page)); | 	m_pal.pages.emplace(m_dstIdx, ox::sfmt("Page {}", m_pal.pages.size() + 1), std::move(m_page)); | ||||||
| 	return {}; | 	return {}; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ | |||||||
| #include <keel/media.hpp> | #include <keel/media.hpp> | ||||||
|  |  | ||||||
| #include "commands/addcolorcommand.hpp" | #include "commands/addcolorcommand.hpp" | ||||||
|  | #include "commands/addpagecommand.hpp" | ||||||
| #include "commands/applycolorallpagescommand.hpp" | #include "commands/applycolorallpagescommand.hpp" | ||||||
| #include "commands/duplicatepagecommand.hpp" | #include "commands/duplicatepagecommand.hpp" | ||||||
| #include "commands/movecolorcommand.hpp" | #include "commands/movecolorcommand.hpp" | ||||||
| @@ -196,7 +197,11 @@ void PaletteEditorImGui::drawPagesEditor() noexcept { | |||||||
| 	constexpr auto toolbarHeight = 40; | 	constexpr auto toolbarHeight = 40; | ||||||
| 	auto const btnSz = ImVec2{paneSz.x / 4 - 5.5f, 24}; | 	auto const btnSz = ImVec2{paneSz.x / 4 - 5.5f, 24}; | ||||||
| 	if (ImGui::Button("Add", btnSz)) { | 	if (ImGui::Button("Add", btnSz)) { | ||||||
|  | 		if (m_pal.pages.empty()) { | ||||||
|  | 			std::ignore = pushCommand<AddPageCommand>(m_pal); | ||||||
|  | 		} else { | ||||||
| 			std::ignore = pushCommand<DuplicatePageCommand>(m_pal, 0u, m_pal.pages.size()); | 			std::ignore = pushCommand<DuplicatePageCommand>(m_pal, 0u, m_pal.pages.size()); | ||||||
|  | 		} | ||||||
| 		m_page = m_pal.pages.size() - 1; | 		m_page = m_pal.pages.size() - 1; | ||||||
| 	} | 	} | ||||||
| 	ImGui::SameLine(); | 	ImGui::SameLine(); | ||||||
|   | |||||||
| @@ -9,27 +9,24 @@ namespace nostalgia::core { | |||||||
| core::UpdateSubSheetCommand::UpdateSubSheetCommand( | core::UpdateSubSheetCommand::UpdateSubSheetCommand( | ||||||
| 		TileSheet &img, | 		TileSheet &img, | ||||||
| 		TileSheet::SubSheetIdx idx, | 		TileSheet::SubSheetIdx idx, | ||||||
| 		ox::String name, | 		ox::StringParam name, | ||||||
| 		int cols, | 		int const cols, | ||||||
| 		int rows) noexcept: | 		int const rows): | ||||||
| 		m_img(img), | 		m_img{img}, | ||||||
| 		m_idx(std::move(idx)), | 		m_idx{std::move(idx)}, | ||||||
| 		m_sheet(getSubSheet(m_img, m_idx)), | 		m_sheet{getSubSheet(m_img, m_idx)} { | ||||||
| 		m_newName(std::move(name)), | 	m_sheet = getSubSheet(m_img, m_idx); | ||||||
| 		m_newCols(cols), | 	m_sheet.name = std::move(name); | ||||||
| 		m_newRows(rows) { | 	OX_THROW_ERROR(resizeSubsheet(m_sheet, m_img.bpp, {cols, rows})); | ||||||
| } | } | ||||||
|  |  | ||||||
| ox::Error UpdateSubSheetCommand::redo() noexcept { | ox::Error UpdateSubSheetCommand::redo() noexcept { | ||||||
| 	auto &sheet = getSubSheet(m_img, m_idx); | 	std::swap(m_sheet, getSubSheet(m_img, m_idx)); | ||||||
| 	sheet.name = m_newName; |  | ||||||
| 	oxLogError(resizeSubsheet(sheet, m_img.bpp, {m_newCols, m_newRows})); |  | ||||||
| 	return {}; | 	return {}; | ||||||
| } | } | ||||||
|  |  | ||||||
| ox::Error UpdateSubSheetCommand::undo() noexcept { | ox::Error UpdateSubSheetCommand::undo() noexcept { | ||||||
| 	auto &sheet = getSubSheet(m_img, m_idx); | 	std::swap(m_sheet, getSubSheet(m_img, m_idx)); | ||||||
| 	sheet = m_sheet; |  | ||||||
| 	return {}; | 	return {}; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -13,17 +13,14 @@ class UpdateSubSheetCommand: public TileSheetCommand { | |||||||
| 		TileSheet &m_img; | 		TileSheet &m_img; | ||||||
| 		TileSheet::SubSheetIdx m_idx; | 		TileSheet::SubSheetIdx m_idx; | ||||||
| 		TileSheet::SubSheet m_sheet; | 		TileSheet::SubSheet m_sheet; | ||||||
| 		ox::String m_newName; |  | ||||||
| 		int m_newCols = 0; |  | ||||||
| 		int m_newRows = 0; |  | ||||||
|  |  | ||||||
| 	public: | 	public: | ||||||
| 		UpdateSubSheetCommand( | 		UpdateSubSheetCommand( | ||||||
| 				TileSheet &img, | 				TileSheet &img, | ||||||
| 				TileSheet::SubSheetIdx idx, | 				TileSheet::SubSheetIdx idx, | ||||||
| 				ox::String name, | 				ox::StringParam name, | ||||||
| 				int cols, | 				int cols, | ||||||
| 				int rows) noexcept; | 				int rows); | ||||||
|  |  | ||||||
| 		ox::Error redo() noexcept final; | 		ox::Error redo() noexcept final; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -234,7 +234,7 @@ void TileSheetEditorImGui::draw(studio::StudioContext&) noexcept { | |||||||
| 		ImGui::BeginChild("SubSheets", {s_palViewWidth - 24, ySize / 2.f}, true); | 		ImGui::BeginChild("SubSheets", {s_palViewWidth - 24, ySize / 2.f}, true); | ||||||
| 		{ | 		{ | ||||||
| 			static constexpr auto btnHeight = ig::BtnSz.y; | 			static constexpr auto btnHeight = ig::BtnSz.y; | ||||||
| 			auto const btnSize = ImVec2{btnHeight, btnHeight}; | 			auto constexpr btnSize = ImVec2{btnHeight, btnHeight}; | ||||||
| 			if (ig::PushButton("+", btnSize)) { | 			if (ig::PushButton("+", btnSize)) { | ||||||
| 				auto insertOnIdx = m_model.activeSubSheetIdx(); | 				auto insertOnIdx = m_model.activeSubSheetIdx(); | ||||||
| 				auto const&parent = m_model.activeSubSheet(); | 				auto const&parent = m_model.activeSubSheet(); | ||||||
| @@ -258,7 +258,10 @@ void TileSheetEditorImGui::draw(studio::StudioContext&) noexcept { | |||||||
| 				m_exportMenu.show(); | 				m_exportMenu.show(); | ||||||
| 			} | 			} | ||||||
| 			TileSheet::SubSheetIdx path; | 			TileSheet::SubSheetIdx path; | ||||||
| 			static constexpr auto flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody; | 			static constexpr auto flags = | ||||||
|  | 				ImGuiTableFlags_RowBg | | ||||||
|  |                 ImGuiTableFlags_NoBordersInBody | | ||||||
|  |                 ImGuiTableFlags_ScrollY; | ||||||
|             if (ImGui::BeginTable("Subsheets", 4, flags)) { |             if (ImGui::BeginTable("Subsheets", 4, flags)) { | ||||||
|                 ImGui::TableSetupColumn("Subsheet", ImGuiTableColumnFlags_NoHide); |                 ImGui::TableSetupColumn("Subsheet", ImGuiTableColumnFlags_NoHide); | ||||||
|                 ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 25); |                 ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 25); | ||||||
| @@ -462,8 +465,12 @@ void TileSheetEditorImGui::drawPaletteMenu() noexcept { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	// header | 	// header | ||||||
|  | 	auto constexpr palTblFlags = | ||||||
|  | 		ImGuiTableFlags_RowBg | | ||||||
|  |         ImGuiTableFlags_SizingStretchProp | | ||||||
|  |         ImGuiTableFlags_ScrollY; | ||||||
| 	if (ImGui::BeginTable( | 	if (ImGui::BeginTable( | ||||||
| 			"PaletteTable", 4, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingStretchProp)) { | 			"PaletteTable", 4, palTblFlags)) { | ||||||
| 		ImGui::TableSetupColumn("Idx", 0, 0.6f); | 		ImGui::TableSetupColumn("Idx", 0, 0.6f); | ||||||
| 		ImGui::TableSetupColumn("", 0, 0.22f); | 		ImGui::TableSetupColumn("", 0, 0.22f); | ||||||
| 		ImGui::TableSetupColumn("Name", 0, 3); | 		ImGui::TableSetupColumn("Name", 0, 3); | ||||||
| @@ -544,7 +551,7 @@ void TileSheetEditorImGui::SubSheetEditor::draw(turbine::Context &tctx) noexcept | |||||||
| 	auto const popupHeight = modSize ? 130.f : 85.f; | 	auto const popupHeight = modSize ? 130.f : 85.f; | ||||||
| 	auto const popupSz = ImVec2{popupWidth, popupHeight}; | 	auto const popupSz = ImVec2{popupWidth, popupHeight}; | ||||||
| 	if (ig::BeginPopup(tctx, popupName, m_show, popupSz)) { | 	if (ig::BeginPopup(tctx, popupName, m_show, popupSz)) { | ||||||
| 		ImGui::InputText("Name", m_name.data(), m_name.cap()); | 		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); | ||||||
|   | |||||||
| @@ -26,11 +26,6 @@ | |||||||
|  |  | ||||||
| namespace nostalgia::core { | namespace nostalgia::core { | ||||||
|  |  | ||||||
| Palette const TileSheetEditorModel::s_defaultPalette = { |  | ||||||
| 	.colorNames = {ox::Vector<ox::String>{{}}}, |  | ||||||
| 	.pages = {{"Page 1", ox::Vector<Color16>(128)}}, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| // delete pixels of all non-leaf nodes | // delete pixels of all non-leaf nodes | ||||||
| static void normalizeSubsheets(TileSheet::SubSheet &ss) noexcept { | static void normalizeSubsheets(TileSheet::SubSheet &ss) noexcept { | ||||||
| 	if (ss.subsheets.empty()) { | 	if (ss.subsheets.empty()) { | ||||||
| @@ -42,7 +37,14 @@ static void normalizeSubsheets(TileSheet::SubSheet &ss) noexcept { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| TileSheetEditorModel::TileSheetEditorModel(studio::StudioContext &sctx, ox::StringView path, studio::UndoStack &undoStack): |  | ||||||
|  | Palette const TileSheetEditorModel::s_defaultPalette = { | ||||||
|  | 	.colorNames = {ox::Vector<ox::String>{{}}}, | ||||||
|  | 	.pages = {{"Page 1", ox::Vector<Color16>(128)}}, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | TileSheetEditorModel::TileSheetEditorModel( | ||||||
|  | 		studio::StudioContext &sctx, ox::StringViewCR path, studio::UndoStack &undoStack): | ||||||
| 	m_sctx(sctx), | 	m_sctx(sctx), | ||||||
| 	m_tctx(m_sctx.tctx), | 	m_tctx(m_sctx.tctx), | ||||||
| 	m_path(path), | 	m_path(path), | ||||||
| @@ -62,7 +64,7 @@ void TileSheetEditorModel::cut() { | |||||||
| 	TileSheetClipboard blankCb; | 	TileSheetClipboard blankCb; | ||||||
| 	auto cb = ox::make_unique<TileSheetClipboard>(); | 	auto cb = ox::make_unique<TileSheetClipboard>(); | ||||||
| 	auto const&s = activeSubSheet(); | 	auto const&s = activeSubSheet(); | ||||||
| 	iterateSelectionRows(*m_selection, [&](int x, int y) { | 	iterateSelectionRows(*m_selection, [&](int const x, int const y) { | ||||||
| 		auto pt = ox::Point{x, y}; | 		auto pt = ox::Point{x, y}; | ||||||
| 		auto const idx = core::idx(s, pt); | 		auto const idx = core::idx(s, pt); | ||||||
| 		auto const c = getPixel(s, m_img.bpp, idx); | 		auto const c = getPixel(s, m_img.bpp, idx); | ||||||
| @@ -73,7 +75,8 @@ void TileSheetEditorModel::cut() { | |||||||
| 	auto const pt1 = m_selection->a; | 	auto const pt1 = m_selection->a; | ||||||
| 	auto const pt2 = ox::Point{s.columns * TileWidth, s.rows * TileHeight}; | 	auto const pt2 = ox::Point{s.columns * TileWidth, s.rows * TileHeight}; | ||||||
| 	turbine::setClipboardObject(m_tctx, std::move(cb)); | 	turbine::setClipboardObject(m_tctx, std::move(cb)); | ||||||
| 	pushCommand(ox::make<CutPasteCommand>(CommandId::Cut, m_img, m_activeSubsSheetIdx, pt1, pt2, blankCb)); | 	pushCommand(ox::make<CutPasteCommand>( | ||||||
|  | 		CommandId::Cut, m_img, m_activeSubsSheetIdx, pt1, pt2, blankCb)); | ||||||
| } | } | ||||||
|  |  | ||||||
| void TileSheetEditorModel::copy() { | void TileSheetEditorModel::copy() { | ||||||
| @@ -81,7 +84,7 @@ void TileSheetEditorModel::copy() { | |||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 	auto cb = ox::make_unique<TileSheetClipboard>(); | 	auto cb = ox::make_unique<TileSheetClipboard>(); | ||||||
| 	iterateSelectionRows(*m_selection, [&](int x, int y) { | 	iterateSelectionRows(*m_selection, [&](int const x, int const y) { | ||||||
| 		auto pt = ox::Point{x, y}; | 		auto pt = ox::Point{x, y}; | ||||||
| 		const auto&s = activeSubSheet(); | 		const auto&s = activeSubSheet(); | ||||||
| 		const auto idx = core::idx(s, pt); | 		const auto idx = core::idx(s, pt); | ||||||
| @@ -105,7 +108,8 @@ void TileSheetEditorModel::paste() { | |||||||
| 	auto const&s = activeSubSheet(); | 	auto const&s = activeSubSheet(); | ||||||
| 	auto const pt1 = m_selection->a; | 	auto const pt1 = m_selection->a; | ||||||
| 	auto const pt2 = ox::Point{s.columns * TileWidth, s.rows * TileHeight}; | 	auto const pt2 = ox::Point{s.columns * TileWidth, s.rows * TileHeight}; | ||||||
| 	pushCommand(ox::make<CutPasteCommand>(CommandId::Paste, m_img, m_activeSubsSheetIdx, pt1, pt2, *cb)); | 	pushCommand(ox::make<CutPasteCommand>( | ||||||
|  | 		CommandId::Paste, m_img, m_activeSubsSheetIdx, pt1, pt2, *cb)); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool TileSheetEditorModel::acceptsClipboardPayload() const noexcept { | bool TileSheetEditorModel::acceptsClipboardPayload() const noexcept { | ||||||
| @@ -120,8 +124,8 @@ ox::StringView TileSheetEditorModel::palPath() const noexcept { | |||||||
| 	} | 	} | ||||||
| 	constexpr ox::StringView uuidPrefix = "uuid://"; | 	constexpr ox::StringView uuidPrefix = "uuid://"; | ||||||
| 	if (ox::beginsWith(path, uuidPrefix)) { | 	if (ox::beginsWith(path, uuidPrefix)) { | ||||||
| 		auto uuid = ox::StringView(&path[uuidPrefix.bytes()], path.bytes() - uuidPrefix.bytes()); | 		auto const uuid = ox::StringView(&path[uuidPrefix.bytes()], path.bytes() - uuidPrefix.bytes()); | ||||||
| 		auto out = keelCtx(m_tctx).uuidToPath.at(uuid); | 		auto const out = keelCtx(m_tctx).uuidToPath.at(uuid); | ||||||
| 		if (out.error) { | 		if (out.error) { | ||||||
| 			return {}; | 			return {}; | ||||||
| 		} | 		} | ||||||
| @@ -131,13 +135,14 @@ ox::StringView TileSheetEditorModel::palPath() const noexcept { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| ox::Error TileSheetEditorModel::setPalette(ox::StringView path) noexcept { | ox::Error TileSheetEditorModel::setPalette(ox::StringViewCR path) noexcept { | ||||||
| 	OX_REQUIRE(uuid, keelCtx(m_tctx).pathToUuid.at(path)); | 	OX_REQUIRE(uuid, keelCtx(m_tctx).pathToUuid.at(path)); | ||||||
| 	pushCommand(ox::make<PaletteChangeCommand>(activeSubSheetIdx(), m_img, uuid->toString())); | 	pushCommand(ox::make<PaletteChangeCommand>( | ||||||
|  | 		activeSubSheetIdx(), m_img, uuid->toString())); | ||||||
| 	return {}; | 	return {}; | ||||||
| } | } | ||||||
|  |  | ||||||
| void TileSheetEditorModel::setPalettePage(size_t pg) noexcept { | void TileSheetEditorModel::setPalettePage(size_t const pg) noexcept { | ||||||
| 	m_palettePage = ox::clamp<size_t>(pg, 0, m_pal->pages.size() - 1); | 	m_palettePage = ox::clamp<size_t>(pg, 0, m_pal->pages.size() - 1); | ||||||
| 	m_updated = true; | 	m_updated = true; | ||||||
| } | } | ||||||
| @@ -146,7 +151,7 @@ size_t TileSheetEditorModel::palettePage() const noexcept { | |||||||
| 	return m_palettePage; | 	return m_palettePage; | ||||||
| } | } | ||||||
|  |  | ||||||
| void TileSheetEditorModel::drawCommand(ox::Point const&pt, std::size_t palIdx) noexcept { | void TileSheetEditorModel::drawCommand(ox::Point const&pt, std::size_t const palIdx) noexcept { | ||||||
| 	const auto &activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx); | 	const auto &activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx); | ||||||
| 	if (pt.x >= activeSubSheet.columns * TileWidth || pt.y >= activeSubSheet.rows * TileHeight) { | 	if (pt.x >= activeSubSheet.columns * TileWidth || pt.y >= activeSubSheet.rows * TileHeight) { | ||||||
| 		return; | 		return; | ||||||
| @@ -155,7 +160,8 @@ void TileSheetEditorModel::drawCommand(ox::Point const&pt, std::size_t palIdx) n | |||||||
| 	if (m_ongoingDrawCommand) { | 	if (m_ongoingDrawCommand) { | ||||||
| 		m_updated = m_updated || m_ongoingDrawCommand->append(idx); | 		m_updated = m_updated || m_ongoingDrawCommand->append(idx); | ||||||
| 	} else if (getPixel(activeSubSheet, m_img.bpp, idx) != palIdx) { | 	} else if (getPixel(activeSubSheet, m_img.bpp, idx) != palIdx) { | ||||||
| 		pushCommand(ox::make<DrawCommand>(m_img, m_activeSubsSheetIdx, idx, static_cast<int>(palIdx))); | 		pushCommand(ox::make<DrawCommand>( | ||||||
|  | 			m_img, m_activeSubsSheetIdx, idx, static_cast<int>(palIdx))); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -171,16 +177,20 @@ void TileSheetEditorModel::rmSubsheet(TileSheet::SubSheetIdx const&idx) noexcept | |||||||
| 	pushCommand(ox::make<RmSubSheetCommand>(m_img, idx)); | 	pushCommand(ox::make<RmSubSheetCommand>(m_img, idx)); | ||||||
| } | } | ||||||
|  |  | ||||||
| void TileSheetEditorModel::insertTiles(TileSheet::SubSheetIdx const&idx, std::size_t tileIdx, std::size_t tileCnt) noexcept { | void TileSheetEditorModel::insertTiles( | ||||||
|  | 	TileSheet::SubSheetIdx const&idx, std::size_t const tileIdx, std::size_t const tileCnt) noexcept { | ||||||
| 	pushCommand(ox::make<InsertTilesCommand>(m_img, idx, tileIdx, tileCnt)); | 	pushCommand(ox::make<InsertTilesCommand>(m_img, idx, tileIdx, tileCnt)); | ||||||
| } | } | ||||||
|  |  | ||||||
| void TileSheetEditorModel::deleteTiles(TileSheet::SubSheetIdx const&idx, std::size_t tileIdx, std::size_t tileCnt) noexcept { | void TileSheetEditorModel::deleteTiles( | ||||||
|  | 	TileSheet::SubSheetIdx const&idx, std::size_t const tileIdx, std::size_t const tileCnt) noexcept { | ||||||
| 	pushCommand(ox::make<DeleteTilesCommand>(m_img, idx, tileIdx, tileCnt)); | 	pushCommand(ox::make<DeleteTilesCommand>(m_img, idx, tileIdx, tileCnt)); | ||||||
| } | } | ||||||
|  |  | ||||||
| ox::Error TileSheetEditorModel::updateSubsheet(TileSheet::SubSheetIdx const&idx, ox::StringView const&name, int cols, int rows) noexcept { | ox::Error TileSheetEditorModel::updateSubsheet( | ||||||
| 	pushCommand(ox::make<UpdateSubSheetCommand>(m_img, idx, ox::String(name), cols, rows)); | 	TileSheet::SubSheetIdx const&idx, ox::StringViewCR name, int const cols, int const rows) noexcept { | ||||||
|  | 	OX_REQUIRE(cmd, ox::makeCatch<UpdateSubSheetCommand>(m_img, idx, name, cols, rows)); | ||||||
|  | 	pushCommand(cmd); | ||||||
| 	return {}; | 	return {}; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -189,7 +199,7 @@ void TileSheetEditorModel::setActiveSubsheet(TileSheet::SubSheetIdx const&idx) n | |||||||
| 	this->activeSubsheetChanged.emit(m_activeSubsSheetIdx); | 	this->activeSubsheetChanged.emit(m_activeSubsSheetIdx); | ||||||
| } | } | ||||||
|  |  | ||||||
| void TileSheetEditorModel::fill(ox::Point const&pt, int palIdx) noexcept { | void TileSheetEditorModel::fill(ox::Point const&pt, int const palIdx) noexcept { | ||||||
| 	auto const&activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx); | 	auto const&activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx); | ||||||
| 	// build idx list | 	// build idx list | ||||||
| 	if (pt.x >= activeSubSheet.columns * TileWidth || pt.y >= activeSubSheet.rows * TileHeight) { | 	if (pt.x >= activeSubSheet.columns * TileWidth || pt.y >= activeSubSheet.rows * TileHeight) { | ||||||
| @@ -197,10 +207,10 @@ void TileSheetEditorModel::fill(ox::Point const&pt, int palIdx) noexcept { | |||||||
| 	} | 	} | ||||||
| 	ox::Array<bool, PixelsPerTile> updateMap = {}; | 	ox::Array<bool, PixelsPerTile> updateMap = {}; | ||||||
| 	auto const oldColor = getPixel(activeSubSheet, m_img.bpp, pt); | 	auto const oldColor = getPixel(activeSubSheet, m_img.bpp, pt); | ||||||
| 	getFillPixels(updateMap, pt, oldColor); | 	getFillPixels(activeSubSheet, updateMap, pt, oldColor); | ||||||
| 	ox::Vector<std::size_t> idxList; | 	ox::Vector<std::size_t> idxList; | ||||||
| 	auto i = core::idx(activeSubSheet, pt) / PixelsPerTile * PixelsPerTile; | 	auto i = core::idx(activeSubSheet, pt) / PixelsPerTile * PixelsPerTile; | ||||||
| 	for (auto u : updateMap) { | 	for (auto const u : updateMap) { | ||||||
| 		if (u) { | 		if (u) { | ||||||
| 			idxList.emplace_back(i); | 			idxList.emplace_back(i); | ||||||
| 		} | 		} | ||||||
| @@ -230,7 +240,7 @@ void TileSheetEditorModel::completeSelection() noexcept { | |||||||
| 		m_selTracker.finishSelection(); | 		m_selTracker.finishSelection(); | ||||||
| 		m_selection.emplace(m_selTracker.selection()); | 		m_selection.emplace(m_selTracker.selection()); | ||||||
| 		auto&pt = m_selection->b; | 		auto&pt = m_selection->b; | ||||||
| 		auto&s = activeSubSheet(); | 		auto const&s = activeSubSheet(); | ||||||
| 		pt.x = ox::min(s.columns * TileWidth - 1, pt.x); | 		pt.x = ox::min(s.columns * TileWidth - 1, pt.x); | ||||||
| 		pt.y = ox::min(s.rows * TileHeight - 1, pt.y); | 		pt.y = ox::min(s.rows * TileHeight - 1, pt.y); | ||||||
| 	} | 	} | ||||||
| @@ -275,47 +285,44 @@ ox::Error TileSheetEditorModel::saveFile() noexcept { | |||||||
| 	return m_sctx.project->writeObj(m_path, m_img, ox::ClawFormat::Metal); | 	return m_sctx.project->writeObj(m_path, m_img, ox::ClawFormat::Metal); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool TileSheetEditorModel::pixelSelected(std::size_t idx) const noexcept { | bool TileSheetEditorModel::pixelSelected(std::size_t const idx) const noexcept { | ||||||
| 	auto const&s = activeSubSheet(); | 	auto const&s = activeSubSheet(); | ||||||
| 	auto const pt = idxToPt(static_cast<int>(idx), s.columns); | 	auto const pt = idxToPt(static_cast<int>(idx), s.columns); | ||||||
| 	return m_selection && m_selection->contains(pt); | 	return m_selection && m_selection->contains(pt); | ||||||
| } | } | ||||||
|  |  | ||||||
| void TileSheetEditorModel::getFillPixels(ox::Span<bool> pixels, ox::Point const&pt, int oldColor) const noexcept { | void TileSheetEditorModel::getFillPixels( | ||||||
| 	const auto &activeSubSheet = this->activeSubSheet(); | 		TileSheet::SubSheet const&activeSubSheet, | ||||||
| 	const auto tileIdx = [activeSubSheet](const ox::Point &pt) noexcept { | 		ox::Span<bool> pixels, | ||||||
| 		return ptToIdx(pt, activeSubSheet.columns) / PixelsPerTile; | 		ox::Point const&pt, | ||||||
| 	}; | 		int const oldColor) const noexcept { | ||||||
| 	// get points | 	auto const idx = ptToIdx(pt, activeSubSheet.columns); | ||||||
| 	const auto leftPt = pt + ox::Point(-1, 0); | 	auto const relIdx = idx % PixelsPerTile; | ||||||
| 	const auto rightPt = pt + ox::Point(1, 0); | 	if (pixels[relIdx] || getPixel(activeSubSheet, m_img.bpp, idx) != oldColor) { | ||||||
| 	const auto topPt = pt + ox::Point(0, -1); | 		return; | ||||||
| 	const auto bottomPt = pt + ox::Point(0, 1); | 	} | ||||||
| 	// calculate indices |  | ||||||
| 	const auto idx = ptToIdx(pt, activeSubSheet.columns); |  | ||||||
| 	const auto leftIdx = ptToIdx(leftPt, activeSubSheet.columns); |  | ||||||
| 	const auto rightIdx = ptToIdx(rightPt, activeSubSheet.columns); |  | ||||||
| 	const auto topIdx = ptToIdx(topPt, activeSubSheet.columns); |  | ||||||
| 	const auto bottomIdx = ptToIdx(bottomPt, activeSubSheet.columns); |  | ||||||
| 	const auto tile = tileIdx(pt); |  | ||||||
| 	// mark pixels to update | 	// mark pixels to update | ||||||
| 	pixels[idx % PixelsPerTile] = true; | 	pixels[relIdx] = true; | ||||||
| 	if (!pixels[leftIdx % PixelsPerTile] && tile == tileIdx(leftPt) && getPixel(activeSubSheet, m_img.bpp, leftIdx) == oldColor) { | 	if (pt.x % TileWidth != 0) { | ||||||
| 		getFillPixels(pixels, leftPt, oldColor); | 		auto const leftPt = pt + ox::Point{-1, 0}; | ||||||
|  | 		getFillPixels(activeSubSheet, pixels, leftPt, oldColor); | ||||||
| 	} | 	} | ||||||
| 	if (!pixels[rightIdx % PixelsPerTile] && tile == tileIdx(rightPt) && getPixel(activeSubSheet, m_img.bpp, rightIdx) == oldColor) { | 	if (pt.x % TileWidth != TileWidth - 1) { | ||||||
| 		getFillPixels(pixels, rightPt, oldColor); | 		auto const rightPt = pt + ox::Point{1, 0}; | ||||||
|  | 		getFillPixels(activeSubSheet, pixels, rightPt, oldColor); | ||||||
| 	} | 	} | ||||||
| 	if (!pixels[topIdx % PixelsPerTile] && tile == tileIdx(topPt) && getPixel(activeSubSheet, m_img.bpp, topIdx) == oldColor) { | 	if (pt.y % TileHeight != 0) { | ||||||
| 		getFillPixels(pixels, topPt, oldColor); | 		auto const topPt = pt + ox::Point{0, -1}; | ||||||
|  | 		getFillPixels(activeSubSheet, pixels, topPt, oldColor); | ||||||
| 	} | 	} | ||||||
| 	if (!pixels[bottomIdx % PixelsPerTile] && tile == tileIdx(bottomPt) && getPixel(activeSubSheet, m_img.bpp, bottomIdx) == oldColor) { | 	if (pt.y % TileHeight != TileHeight - 1) { | ||||||
| 		getFillPixels(pixels, bottomPt, oldColor); | 		auto const bottomPt = pt + ox::Point{0, 1}; | ||||||
|  | 		getFillPixels(activeSubSheet, pixels, bottomPt, oldColor); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| void TileSheetEditorModel::pushCommand(studio::UndoCommand *cmd) noexcept { | void TileSheetEditorModel::pushCommand(studio::UndoCommand *cmd) noexcept { | ||||||
| 	std::ignore = m_undoStack.push(ox::UPtr<studio::UndoCommand>(cmd)); | 	std::ignore = m_undoStack.push(ox::UPtr<studio::UndoCommand>{cmd}); | ||||||
| 	m_ongoingDrawCommand = dynamic_cast<DrawCommand*>(cmd); | 	m_ongoingDrawCommand = dynamic_cast<DrawCommand*>(cmd); | ||||||
| 	m_updated = true; | 	m_updated = true; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -4,9 +4,7 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <ox/std/bounds.hpp> |  | ||||||
| #include <ox/std/point.hpp> | #include <ox/std/point.hpp> | ||||||
| #include <ox/std/trace.hpp> |  | ||||||
| #include <ox/std/string.hpp> | #include <ox/std/string.hpp> | ||||||
|  |  | ||||||
| #include <studio/studio.hpp> | #include <studio/studio.hpp> | ||||||
| @@ -38,7 +36,7 @@ class TileSheetEditorModel: public ox::SignalHandler { | |||||||
| 		bool m_updated = false; | 		bool m_updated = false; | ||||||
|  |  | ||||||
| 	public: | 	public: | ||||||
| 		TileSheetEditorModel(studio::StudioContext &sctx, ox::StringView path, studio::UndoStack &undoStack); | 		TileSheetEditorModel(studio::StudioContext &sctx, ox::StringViewCR path, studio::UndoStack &undoStack); | ||||||
|  |  | ||||||
| 		~TileSheetEditorModel() override = default; | 		~TileSheetEditorModel() override = default; | ||||||
|  |  | ||||||
| @@ -63,7 +61,7 @@ class TileSheetEditorModel: public ox::SignalHandler { | |||||||
| 		[[nodiscard]] | 		[[nodiscard]] | ||||||
| 		ox::StringView palPath() const noexcept; | 		ox::StringView palPath() const noexcept; | ||||||
|  |  | ||||||
| 		ox::Error setPalette(ox::StringView path) noexcept; | 		ox::Error setPalette(ox::StringViewCR path) noexcept; | ||||||
|  |  | ||||||
| 		void setPalettePage(size_t pg) noexcept; | 		void setPalettePage(size_t pg) noexcept; | ||||||
|  |  | ||||||
| @@ -128,7 +126,11 @@ class TileSheetEditorModel: public ox::SignalHandler { | |||||||
| 		bool pixelSelected(std::size_t idx) const noexcept; | 		bool pixelSelected(std::size_t idx) const noexcept; | ||||||
|  |  | ||||||
| 	private: | 	private: | ||||||
| 		void getFillPixels(ox::Span<bool> pixels, ox::Point const&pt, int oldColor) const noexcept; | 		void getFillPixels( | ||||||
|  | 			TileSheet::SubSheet const&activeSubSheet, | ||||||
|  | 			ox::Span<bool> pixels, | ||||||
|  | 			ox::Point const&pt, | ||||||
|  | 			int oldColor) const noexcept; | ||||||
|  |  | ||||||
| 		void pushCommand(studio::UndoCommand *cmd) noexcept; | 		void pushCommand(studio::UndoCommand *cmd) noexcept; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ target_link_libraries( | |||||||
|  |  | ||||||
| target_compile_definitions( | target_compile_definitions( | ||||||
| 	NostalgiaStudio PUBLIC | 	NostalgiaStudio PUBLIC | ||||||
| 		OLYMPIC_APP_VERSION="d2024.12.1" | 		OLYMPIC_APP_VERSION="d2024.12.3" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| install( | install( | ||||||
|   | |||||||
| @@ -50,7 +50,7 @@ void NewProject::draw(studio::StudioContext &ctx) noexcept { | |||||||
|  |  | ||||||
| void NewProject::drawNewProjectName(studio::StudioContext &sctx) noexcept { | void NewProject::drawNewProjectName(studio::StudioContext &sctx) noexcept { | ||||||
| 	drawWindow(sctx.tctx, &m_open, [this, &sctx] { | 	drawWindow(sctx.tctx, &m_open, [this, &sctx] { | ||||||
| 		ImGui::InputText("Name", m_projectName.data(), m_projectName.cap()); | 		ig::InputText("Name", m_projectName); | ||||||
| 		ImGui::Text("Path: %s", m_projectPath.c_str()); | 		ImGui::Text("Path: %s", m_projectPath.c_str()); | ||||||
| 		if (ImGui::Button("Browse")) { | 		if (ImGui::Button("Browse")) { | ||||||
| 			oxLogError(studio::chooseDirectory().moveTo(m_projectPath)); | 			oxLogError(studio::chooseDirectory().moveTo(m_projectPath)); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user