Compare commits
	
		
			7 Commits
		
	
	
		
			819e93bb1c
			...
			e6803af22f
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| e6803af22f | |||
| db961739ad | |||
| 2167a46266 | |||
| d1c6ab9f77 | |||
| c426decd99 | |||
| 496afd66ea | |||
| ad7f696a71 | 
							
								
								
									
										2
									
								
								deps/glutils/include/glutils/glutils.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								deps/glutils/include/glutils/glutils.hpp
									
									
									
									
										vendored
									
									
								
							@@ -111,7 +111,7 @@ extern template struct GLObject<deleteProgram>;
 | 
			
		||||
extern template struct GLObject<deleteShader>;
 | 
			
		||||
 | 
			
		||||
using GLBuffer = GLObject<deleteBuffer>;
 | 
			
		||||
using GLFrameBuffer = GLObject<deleteBuffer>;
 | 
			
		||||
using GLFrameBuffer = GLObject<deleteFrameBuffer>;
 | 
			
		||||
using GLRenderBuffer = GLObject<deleteRenderBuffer>;
 | 
			
		||||
using GLShader = GLObject<deleteShader>;
 | 
			
		||||
using GLProgram = GLObject<deleteProgram>;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								deps/ox/src/ox/fs/filesystem/filelocation.cpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								deps/ox/src/ox/fs/filesystem/filelocation.cpp
									
									
									
									
										vendored
									
									
								
							@@ -45,9 +45,14 @@ FileAddress &FileAddress::operator=(const FileAddress &other) noexcept {
 | 
			
		||||
	switch (m_type) {
 | 
			
		||||
		case FileAddressType::Path:
 | 
			
		||||
		{
 | 
			
		||||
			auto strSize = ox_strlen(other.m_data.path) + 1;
 | 
			
		||||
			m_data.path = new char[strSize];
 | 
			
		||||
			ox_memcpy(m_data.path, other.m_data.path, strSize);
 | 
			
		||||
			if (other.m_data.path) {
 | 
			
		||||
				auto strSize = ox_strlen(other.m_data.path) + 1;
 | 
			
		||||
				m_data.path = new char[strSize];
 | 
			
		||||
				ox_memcpy(m_data.path, other.m_data.path, strSize);
 | 
			
		||||
			} else {
 | 
			
		||||
				m_data.constPath = "";
 | 
			
		||||
				m_type = FileAddressType::ConstPath;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		case FileAddressType::ConstPath:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										27
									
								
								deps/ox/src/ox/model/modelhandleradaptor.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										27
									
								
								deps/ox/src/ox/model/modelhandleradaptor.hpp
									
									
									
									
										vendored
									
									
								
							@@ -47,7 +47,11 @@ class ModelHandlerInterface {
 | 
			
		||||
 | 
			
		||||
		template<std::size_t len>
 | 
			
		||||
		constexpr Error fieldCString(const char *name, const char val[len]) noexcept requires(opType_v != OpType::Read) {
 | 
			
		||||
			return m_handler->fieldCString(name, &val[0], len);
 | 
			
		||||
			if constexpr(opType_v != OpType::Read) {
 | 
			
		||||
				return m_handler->fieldCString(name, &val[0], len);
 | 
			
		||||
			} else {
 | 
			
		||||
				return {};
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		constexpr Error fieldCString(const char *name, char **val) noexcept {
 | 
			
		||||
@@ -55,11 +59,21 @@ class ModelHandlerInterface {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		constexpr Error fieldCString(const char *name, const char *const*val) noexcept requires(opType_v != OpType::Read) {
 | 
			
		||||
			return m_handler->fieldCString(name, val);
 | 
			
		||||
			// this check looks pointless, but it's to address a Clang bug
 | 
			
		||||
			if constexpr(opType_v != OpType::Read) {
 | 
			
		||||
				return m_handler->fieldCString(name, val);
 | 
			
		||||
			} else {
 | 
			
		||||
				return {};
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		constexpr Error fieldCString(const char *name, const char **val) noexcept requires(opType_v != OpType::Read) {
 | 
			
		||||
			return m_handler->fieldCString(name, val);
 | 
			
		||||
			// this check looks pointless, but it's to address a Clang bug
 | 
			
		||||
			if constexpr(opType_v != OpType::Read) {
 | 
			
		||||
				return m_handler->fieldCString(name, val);
 | 
			
		||||
			} else {
 | 
			
		||||
				return {};
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		constexpr Error fieldCString(const char *name, char **val, std::size_t buffLen) noexcept {
 | 
			
		||||
@@ -67,7 +81,12 @@ class ModelHandlerInterface {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		constexpr Error fieldCString(const char *name, const char **val, std::size_t buffLen) noexcept requires(opType_v != OpType::Read) {
 | 
			
		||||
			return m_handler->fieldCString(name, val, buffLen);
 | 
			
		||||
			// this check looks pointless, but it's to address a Clang bug
 | 
			
		||||
			if constexpr(opType_v != OpType::Read) {
 | 
			
		||||
				return m_handler->fieldCString(name, val, buffLen);
 | 
			
		||||
			} else {
 | 
			
		||||
				return {};
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		constexpr Error fieldCString(const char *name, char *val, std::size_t buffLen) noexcept {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								deps/ox/src/ox/oc/write.cpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								deps/ox/src/ox/oc/write.cpp
									
									
									
									
										vendored
									
									
								
							@@ -27,7 +27,7 @@ Error OrganicClawWriter::fieldCString(const char *key, const char *const*val, in
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error OrganicClawWriter::fieldCString(const char *key, const char *const*val) noexcept {
 | 
			
		||||
	return fieldCString(key, const_cast<const char**>(val), {});
 | 
			
		||||
	return fieldCString(key, const_cast<const char**>(val), static_cast<int>(ox_strlen(val)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error OrganicClawWriter::field(const char *key, const UUID *uuid) noexcept {
 | 
			
		||||
 
 | 
			
		||||
@@ -42,14 +42,15 @@ ox::Error toPngFile(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TileSheetEditorImGui::TileSheetEditorImGui(turbine::Context &ctx, ox::CRStringView path):
 | 
			
		||||
	Editor(path),
 | 
			
		||||
	m_ctx(ctx),
 | 
			
		||||
	m_tileSheetEditor(m_ctx, path, *undoStack()) {
 | 
			
		||||
		Editor(path),
 | 
			
		||||
		m_ctx(ctx),
 | 
			
		||||
		m_view(m_ctx, path, *undoStack()),
 | 
			
		||||
		m_model(m_view.model()) {
 | 
			
		||||
	oxIgnoreError(setPaletteSelection());
 | 
			
		||||
	// connect signal/slots
 | 
			
		||||
	undoStack()->changeTriggered.connect(this, &TileSheetEditorImGui::markUnsavedChanges);
 | 
			
		||||
	m_subsheetEditor.inputSubmitted.connect(this, &TileSheetEditorImGui::updateActiveSubsheet);
 | 
			
		||||
	model()->paletteChanged.connect(this, &TileSheetEditorImGui::setPaletteSelection);
 | 
			
		||||
	m_model.paletteChanged.connect(this, &TileSheetEditorImGui::setPaletteSelection);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TileSheetEditorImGui::exportFile() {
 | 
			
		||||
@@ -57,15 +58,15 @@ void TileSheetEditorImGui::exportFile() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TileSheetEditorImGui::cut() {
 | 
			
		||||
	model()->cut();
 | 
			
		||||
	m_model.cut();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TileSheetEditorImGui::copy() {
 | 
			
		||||
	model()->copy();
 | 
			
		||||
	m_model.copy();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TileSheetEditorImGui::paste() {
 | 
			
		||||
	model()->paste();
 | 
			
		||||
	m_model.paste();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TileSheetEditorImGui::keyStateChanged(turbine::Key key, bool down) {
 | 
			
		||||
@@ -75,23 +76,32 @@ void TileSheetEditorImGui::keyStateChanged(turbine::Key key, bool down) {
 | 
			
		||||
	if (key == turbine::Key::Escape) {
 | 
			
		||||
		m_subsheetEditor.close();
 | 
			
		||||
	}
 | 
			
		||||
	auto pal = model()->pal();
 | 
			
		||||
	auto pal = m_model.pal();
 | 
			
		||||
	if (pal) {
 | 
			
		||||
		const auto colorCnt = pal->colors.size();
 | 
			
		||||
		if (key == turbine::Key::Alpha_D) {
 | 
			
		||||
			m_tool = Tool::Draw;
 | 
			
		||||
			model()->clearSelection();
 | 
			
		||||
			setCopyEnabled(false);
 | 
			
		||||
			setCutEnabled(false);
 | 
			
		||||
			setPasteEnabled(false);
 | 
			
		||||
			m_model.clearSelection();
 | 
			
		||||
		} else if (key == turbine::Key::Alpha_S) {
 | 
			
		||||
			m_tool = Tool::Select;
 | 
			
		||||
			setCopyEnabled(true);
 | 
			
		||||
			setCutEnabled(true);
 | 
			
		||||
			setPasteEnabled(true);
 | 
			
		||||
		} else if (key == turbine::Key::Alpha_F) {
 | 
			
		||||
			m_tool = Tool::Fill;
 | 
			
		||||
			model()->clearSelection();
 | 
			
		||||
			setCopyEnabled(false);
 | 
			
		||||
			setCutEnabled(false);
 | 
			
		||||
			setPasteEnabled(false);
 | 
			
		||||
			m_model.clearSelection();
 | 
			
		||||
		} else if (key >= turbine::Key::Num_1 && key <= turbine::Key::Num_9 && key <= turbine::Key::Num_0 + colorCnt) {
 | 
			
		||||
			auto idx = ox::min<std::size_t>(static_cast<uint32_t>(key - turbine::Key::Num_1), colorCnt - 1);
 | 
			
		||||
			m_tileSheetEditor.setPalIdx(idx);
 | 
			
		||||
			m_view.setPalIdx(idx);
 | 
			
		||||
		} else if (key == turbine::Key::Num_0 && colorCnt >= 10) {
 | 
			
		||||
			auto idx = ox::min<std::size_t>(static_cast<uint32_t>(key - turbine::Key::Num_1 + 9), colorCnt - 1);
 | 
			
		||||
			m_tileSheetEditor.setPalIdx(idx);
 | 
			
		||||
			m_view.setPalIdx(idx);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -118,12 +128,12 @@ void TileSheetEditorImGui::draw(turbine::Context&) noexcept {
 | 
			
		||||
			ImGui::SameLine();
 | 
			
		||||
			if (ImGui::Selectable("Draw", m_tool == Tool::Draw, 0, btnSz)) {
 | 
			
		||||
				m_tool = Tool::Draw;
 | 
			
		||||
				model()->clearSelection();
 | 
			
		||||
				m_model.clearSelection();
 | 
			
		||||
			}
 | 
			
		||||
			ImGui::SameLine();
 | 
			
		||||
			if (ImGui::Selectable("Fill", m_tool == Tool::Fill, 0, btnSz)) {
 | 
			
		||||
				m_tool = Tool::Fill;
 | 
			
		||||
				model()->clearSelection();
 | 
			
		||||
				m_model.clearSelection();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		ImGui::EndChild();
 | 
			
		||||
@@ -139,17 +149,17 @@ void TileSheetEditorImGui::draw(turbine::Context&) noexcept {
 | 
			
		||||
			static constexpr auto btnHeight = 18;
 | 
			
		||||
			const auto btnSize = ImVec2(18, btnHeight);
 | 
			
		||||
			if (ImGui::Button("+", btnSize)) {
 | 
			
		||||
				auto insertOnIdx = model()->activeSubSheetIdx();
 | 
			
		||||
				const auto &parent = *model()->activeSubSheet();
 | 
			
		||||
				model()->addSubsheet(insertOnIdx);
 | 
			
		||||
				auto insertOnIdx = m_model.activeSubSheetIdx();
 | 
			
		||||
				const auto &parent = *m_model.activeSubSheet();
 | 
			
		||||
				m_model.addSubsheet(insertOnIdx);
 | 
			
		||||
				insertOnIdx.emplace_back(parent.subsheets.size() - 1);
 | 
			
		||||
				model()->setActiveSubsheet(insertOnIdx);
 | 
			
		||||
				m_model.setActiveSubsheet(insertOnIdx);
 | 
			
		||||
			}
 | 
			
		||||
			ImGui::SameLine();
 | 
			
		||||
			if (ImGui::Button("-", btnSize)) {
 | 
			
		||||
				const auto &activeSubsheetIdx = model()->activeSubSheetIdx();
 | 
			
		||||
				const auto &activeSubsheetIdx = m_model.activeSubSheetIdx();
 | 
			
		||||
				if (activeSubsheetIdx.size() > 0) {
 | 
			
		||||
					model()->rmSubsheet(activeSubsheetIdx);
 | 
			
		||||
					m_model.rmSubsheet(activeSubsheetIdx);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			ImGui::SameLine();
 | 
			
		||||
@@ -167,7 +177,7 @@ void TileSheetEditorImGui::draw(turbine::Context&) noexcept {
 | 
			
		||||
				ImGui::TableSetupColumn("Columns", ImGuiTableColumnFlags_WidthFixed, 50);
 | 
			
		||||
				ImGui::TableSetupColumn("Rows", ImGuiTableColumnFlags_WidthFixed, 50);
 | 
			
		||||
				ImGui::TableHeadersRow();
 | 
			
		||||
				drawSubsheetSelector(&m_tileSheetEditor.img().subsheet, &path);
 | 
			
		||||
				drawSubsheetSelector(&m_view.img().subsheet, &path);
 | 
			
		||||
				ImGui::EndTable();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -182,7 +192,7 @@ void TileSheetEditorImGui::drawSubsheetSelector(TileSheet::SubSheet *subsheet, T
 | 
			
		||||
	using Str = ox::BasicString<100>;
 | 
			
		||||
	auto pathStr = ox::join<Str>("##", *path).value;
 | 
			
		||||
	auto lbl = ox::sfmt<Str>("{}##{}", subsheet->name, pathStr);
 | 
			
		||||
	const auto rowSelected = *path == model()->activeSubSheetIdx();
 | 
			
		||||
	const auto rowSelected = *path == m_model.activeSubSheetIdx();
 | 
			
		||||
	const auto flags = ImGuiTreeNodeFlags_SpanFullWidth
 | 
			
		||||
	                 | ImGuiTreeNodeFlags_OpenOnArrow
 | 
			
		||||
	                 | ImGuiTreeNodeFlags_DefaultOpen
 | 
			
		||||
@@ -192,7 +202,7 @@ void TileSheetEditorImGui::drawSubsheetSelector(TileSheet::SubSheet *subsheet, T
 | 
			
		||||
	const auto open = ImGui::TreeNodeEx(lbl.c_str(), flags);
 | 
			
		||||
	ImGui::SameLine();
 | 
			
		||||
	if (ImGui::IsItemClicked()) {
 | 
			
		||||
		model()->setActiveSubsheet(*path);
 | 
			
		||||
		m_model.setActiveSubsheet(*path);
 | 
			
		||||
	}
 | 
			
		||||
	if (ImGui::IsMouseDoubleClicked(0) && ImGui::IsItemHovered()) {
 | 
			
		||||
		showSubsheetEditor();
 | 
			
		||||
@@ -229,11 +239,11 @@ ox::Vec2 TileSheetEditorImGui::clickPos(ImVec2 const&winPos, ox::Vec2 clickPos)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ox::Error TileSheetEditorImGui::saveItem() noexcept {
 | 
			
		||||
	return model()->saveFile();
 | 
			
		||||
	return m_model.saveFile();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TileSheetEditorImGui::showSubsheetEditor() noexcept {
 | 
			
		||||
	const auto sheet = model()->activeSubSheet();
 | 
			
		||||
	const auto sheet = m_model.activeSubSheet();
 | 
			
		||||
	if (sheet->subsheets.size()) {
 | 
			
		||||
		m_subsheetEditor.show(sheet->name, -1, -1);
 | 
			
		||||
	} else {
 | 
			
		||||
@@ -247,9 +257,9 @@ void TileSheetEditorImGui::exportSubhseetToPng() noexcept {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	// subsheet to png
 | 
			
		||||
	const auto &img = model()->img();
 | 
			
		||||
	const auto &s = *model()->activeSubSheet();
 | 
			
		||||
	const auto &pal = model()->pal();
 | 
			
		||||
	const auto &img = m_model.img();
 | 
			
		||||
	const auto &s = *m_model.activeSubSheet();
 | 
			
		||||
	const auto &pal = m_model.pal();
 | 
			
		||||
	err = toPngFile(path, s, *pal, img.bpp);
 | 
			
		||||
	if (err) {
 | 
			
		||||
		oxErrorf("Tilesheet export failed: {}", toStr(err));
 | 
			
		||||
@@ -261,20 +271,17 @@ void TileSheetEditorImGui::drawTileSheet(ox::Vec2 const&fbSize) noexcept {
 | 
			
		||||
	const auto fbSizei = ox::Size(static_cast<int>(fbSize.x), static_cast<int>(fbSize.y));
 | 
			
		||||
	if (m_framebuffer.width != fbSizei.width || m_framebuffer.height != fbSizei.height) {
 | 
			
		||||
		glutils::resizeInitFrameBuffer(m_framebuffer, fbSizei.width, fbSizei.height);
 | 
			
		||||
		m_tileSheetEditor.resizeView(fbSize);
 | 
			
		||||
	} else if (m_tileSheetEditor.updated()) {
 | 
			
		||||
		m_tileSheetEditor.ackUpdate();
 | 
			
		||||
		m_view.resizeView(fbSize);
 | 
			
		||||
	} else if (m_view.updated()) {
 | 
			
		||||
		m_view.ackUpdate();
 | 
			
		||||
	}
 | 
			
		||||
	glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
 | 
			
		||||
	// clear screen and draw
 | 
			
		||||
	glViewport(0, 0, fbSizei.width, fbSizei.height);
 | 
			
		||||
	m_tileSheetEditor.draw();
 | 
			
		||||
	m_view.draw();
 | 
			
		||||
	glBindFramebuffer(GL_FRAMEBUFFER, 0);
 | 
			
		||||
	ImTextureID buffId{};
 | 
			
		||||
	static_assert(sizeof(ImTextureID) >= sizeof(m_framebuffer.color.id));
 | 
			
		||||
	memcpy(&buffId, &m_framebuffer.color.id, sizeof(m_framebuffer.color.id));
 | 
			
		||||
	ImGui::Image(
 | 
			
		||||
		buffId,
 | 
			
		||||
		std::bit_cast<ImTextureID>(uintptr_t{m_framebuffer.color.id}),
 | 
			
		||||
		static_cast<ImVec2>(fbSize),
 | 
			
		||||
		ImVec2(0, 1),
 | 
			
		||||
		ImVec2(1, 0));
 | 
			
		||||
@@ -287,22 +294,22 @@ void TileSheetEditorImGui::drawTileSheet(ox::Vec2 const&fbSize) noexcept {
 | 
			
		||||
		if (wheel != 0) {
 | 
			
		||||
			const auto zoomMod = ox::defines::OS == ox::OS::Darwin ?
 | 
			
		||||
			                     io.KeySuper : turbine::buttonDown(m_ctx, turbine::Key::Mod_Ctrl);
 | 
			
		||||
			m_tileSheetEditor.scrollV(fbSize, wheel, zoomMod);
 | 
			
		||||
			m_view.scrollV(fbSize, wheel, zoomMod);
 | 
			
		||||
		}
 | 
			
		||||
		if (wheelh != 0) {
 | 
			
		||||
			m_tileSheetEditor.scrollH(fbSize, wheelh);
 | 
			
		||||
			m_view.scrollH(fbSize, wheelh);
 | 
			
		||||
		}
 | 
			
		||||
		if (io.MouseDown[0] && m_prevMouseDownPos != mousePos) {
 | 
			
		||||
			m_prevMouseDownPos = mousePos;
 | 
			
		||||
			switch (m_tool) {
 | 
			
		||||
				case Tool::Draw:
 | 
			
		||||
					m_tileSheetEditor.clickDraw(fbSize, clickPos(winPos, mousePos));
 | 
			
		||||
					m_view.clickDraw(fbSize, clickPos(winPos, mousePos));
 | 
			
		||||
					break;
 | 
			
		||||
				case Tool::Fill:
 | 
			
		||||
					m_tileSheetEditor.clickFill(fbSize, clickPos(winPos, mousePos));
 | 
			
		||||
					m_view.clickFill(fbSize, clickPos(winPos, mousePos));
 | 
			
		||||
					break;
 | 
			
		||||
				case Tool::Select:
 | 
			
		||||
					m_tileSheetEditor.clickSelect(fbSize, clickPos(winPos, mousePos));
 | 
			
		||||
					m_view.clickSelect(fbSize, clickPos(winPos, mousePos));
 | 
			
		||||
					break;
 | 
			
		||||
				case Tool::None:
 | 
			
		||||
					break;
 | 
			
		||||
@@ -312,22 +319,22 @@ void TileSheetEditorImGui::drawTileSheet(ox::Vec2 const&fbSize) noexcept {
 | 
			
		||||
	if (ImGui::BeginPopupContextItem("TileMenu", ImGuiPopupFlags_MouseButtonRight)) {
 | 
			
		||||
		const auto popupPos = ox::Vec2(ImGui::GetWindowPos());
 | 
			
		||||
		if (ImGui::MenuItem("Insert Tile")) {
 | 
			
		||||
			m_tileSheetEditor.insertTile(fbSize, clickPos(winPos, popupPos));
 | 
			
		||||
			m_view.insertTile(fbSize, clickPos(winPos, popupPos));
 | 
			
		||||
		}
 | 
			
		||||
		if (ImGui::MenuItem("Delete Tile")) {
 | 
			
		||||
			m_tileSheetEditor.deleteTile(fbSize, clickPos(winPos, popupPos));
 | 
			
		||||
			m_view.deleteTile(fbSize, clickPos(winPos, popupPos));
 | 
			
		||||
		}
 | 
			
		||||
		ImGui::EndPopup();
 | 
			
		||||
	}
 | 
			
		||||
	if (io.MouseReleased[0]) {
 | 
			
		||||
		m_prevMouseDownPos = {-1, -1};
 | 
			
		||||
		m_tileSheetEditor.releaseMouseButton();
 | 
			
		||||
		m_view.releaseMouseButton();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TileSheetEditorImGui::drawPaletteSelector() noexcept {
 | 
			
		||||
	auto sctx = applicationData<studio::StudioContext>(m_ctx);
 | 
			
		||||
	const auto &files = sctx->project->fileList(core::FileExt_npal);
 | 
			
		||||
	auto &sctx = *applicationData<studio::StudioContext>(m_ctx);
 | 
			
		||||
	const auto &files = sctx.project->fileList(core::FileExt_npal);
 | 
			
		||||
	const auto first = m_selectedPaletteIdx < files.size() ?
 | 
			
		||||
		files[m_selectedPaletteIdx].c_str() : "";
 | 
			
		||||
	if (ImGui::BeginCombo("Palette", first, 0)) {
 | 
			
		||||
@@ -335,7 +342,7 @@ void TileSheetEditorImGui::drawPaletteSelector() noexcept {
 | 
			
		||||
			const auto selected = (m_selectedPaletteIdx == n);
 | 
			
		||||
			if (ImGui::Selectable(files[n].c_str(), selected) && m_selectedPaletteIdx != n) {
 | 
			
		||||
				m_selectedPaletteIdx = n;
 | 
			
		||||
				oxLogError(model()->setPalette(files[n]));
 | 
			
		||||
				oxLogError(m_model.setPalette(files[n]));
 | 
			
		||||
			}
 | 
			
		||||
			if (selected) {
 | 
			
		||||
				ImGui::SetItemDefaultFocus();
 | 
			
		||||
@@ -349,15 +356,15 @@ void TileSheetEditorImGui::drawPaletteSelector() noexcept {
 | 
			
		||||
		ImGui::TableSetupColumn("", 0, 0.22f);
 | 
			
		||||
		ImGui::TableSetupColumn("Color16", 0, 3);
 | 
			
		||||
		ImGui::TableHeadersRow();
 | 
			
		||||
		if (auto pal = m_tileSheetEditor.pal()) {
 | 
			
		||||
		if (auto pal = m_view.pal()) {
 | 
			
		||||
			for (auto i = 0u; auto c: pal->colors) {
 | 
			
		||||
				ImGui::PushID(static_cast<int>(i));
 | 
			
		||||
				// Column: color idx
 | 
			
		||||
				ImGui::TableNextColumn();
 | 
			
		||||
				const auto label = ox::BString<8>() + (i + 1);
 | 
			
		||||
				const auto rowSelected = i == m_tileSheetEditor.palIdx();
 | 
			
		||||
				const auto rowSelected = i == m_view.palIdx();
 | 
			
		||||
				if (ImGui::Selectable(label.c_str(), rowSelected, ImGuiSelectableFlags_SpanAllColumns)) {
 | 
			
		||||
					m_tileSheetEditor.setPalIdx(i);
 | 
			
		||||
					m_view.setPalIdx(i);
 | 
			
		||||
				}
 | 
			
		||||
				// Column: color RGB
 | 
			
		||||
				ImGui::TableNextColumn();
 | 
			
		||||
@@ -375,13 +382,13 @@ void TileSheetEditorImGui::drawPaletteSelector() noexcept {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ox::Error TileSheetEditorImGui::updateActiveSubsheet(ox::StringView const&name, int cols, int rows) noexcept {
 | 
			
		||||
	return model()->updateSubsheet(model()->activeSubSheetIdx(), name, cols, rows);
 | 
			
		||||
	return m_model.updateSubsheet(m_model.activeSubSheetIdx(), name, cols, rows);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ox::Error TileSheetEditorImGui::setPaletteSelection() noexcept {
 | 
			
		||||
	const auto &palPath = model()->palPath();
 | 
			
		||||
	auto sctx = applicationData<studio::StudioContext>(m_ctx);
 | 
			
		||||
	const auto &palList = sctx->project->fileList(core::FileExt_npal);
 | 
			
		||||
	const auto &palPath = m_model.palPath();
 | 
			
		||||
	auto &sctx = *applicationData<studio::StudioContext>(m_ctx);
 | 
			
		||||
	const auto &palList = sctx.project->fileList(core::FileExt_npal);
 | 
			
		||||
	for (std::size_t i = 0; const auto &pal : palList) {
 | 
			
		||||
		if (palPath == pal) {
 | 
			
		||||
			m_selectedPaletteIdx = i;
 | 
			
		||||
 
 | 
			
		||||
@@ -47,7 +47,8 @@ class TileSheetEditorImGui: public studio::Editor {
 | 
			
		||||
		ox::Vector<ox::String> m_paletteList;
 | 
			
		||||
		SubSheetEditor m_subsheetEditor;
 | 
			
		||||
		glutils::FrameBuffer m_framebuffer;
 | 
			
		||||
		TileSheetEditorView m_tileSheetEditor;
 | 
			
		||||
		TileSheetEditorView m_view;
 | 
			
		||||
		TileSheetEditorModel &m_model;
 | 
			
		||||
		float m_palViewWidth = 300;
 | 
			
		||||
		ox::Vec2 m_prevMouseDownPos;
 | 
			
		||||
		Tool m_tool = Tool::Draw;
 | 
			
		||||
@@ -82,16 +83,6 @@ class TileSheetEditorImGui: public studio::Editor {
 | 
			
		||||
 | 
			
		||||
		void exportSubhseetToPng() noexcept;
 | 
			
		||||
 | 
			
		||||
		[[nodiscard]]
 | 
			
		||||
		constexpr auto model() const noexcept {
 | 
			
		||||
			return m_tileSheetEditor.model();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[[nodiscard]]
 | 
			
		||||
		constexpr auto model() noexcept {
 | 
			
		||||
			return m_tileSheetEditor.model();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void drawTileSheet(ox::Vec2 const&fbSize) noexcept;
 | 
			
		||||
 | 
			
		||||
		void drawPaletteSelector() noexcept;
 | 
			
		||||
 
 | 
			
		||||
@@ -124,18 +124,6 @@ class TileSheetEditorModel: public ox::SignalHandler {
 | 
			
		||||
	private:
 | 
			
		||||
		void pushCommand(studio::UndoCommand *cmd) noexcept;
 | 
			
		||||
 | 
			
		||||
		void setPalette();
 | 
			
		||||
 | 
			
		||||
		void saveState();
 | 
			
		||||
 | 
			
		||||
		void restoreState();
 | 
			
		||||
 | 
			
		||||
		[[nodiscard]]
 | 
			
		||||
		ox::String paletteName(ox::String const&palettePath) const;
 | 
			
		||||
 | 
			
		||||
		[[nodiscard]]
 | 
			
		||||
		ox::String palettePath(ox::String const&palettePath) const;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
constexpr const TileSheet &TileSheetEditorModel::img() const noexcept {
 | 
			
		||||
 
 | 
			
		||||
@@ -84,13 +84,13 @@ class TileSheetEditorView: public ox::SignalHandler {
 | 
			
		||||
		constexpr const Palette *pal() const noexcept;
 | 
			
		||||
 | 
			
		||||
		[[nodiscard]]
 | 
			
		||||
		constexpr auto *model() noexcept {
 | 
			
		||||
			return &m_model;
 | 
			
		||||
		constexpr auto &model() noexcept {
 | 
			
		||||
			return m_model;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[[nodiscard]]
 | 
			
		||||
		constexpr auto *model() const noexcept {
 | 
			
		||||
			return &m_model;
 | 
			
		||||
		constexpr auto &model() const noexcept {
 | 
			
		||||
			return m_model;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		constexpr auto setPalIdx(auto palIdx) noexcept {
 | 
			
		||||
 
 | 
			
		||||
@@ -53,7 +53,7 @@ static ox::Error runApp(
 | 
			
		||||
	turbine::setConstantRefresh(*ctx, false);
 | 
			
		||||
	studio::StudioContext studioCtx;
 | 
			
		||||
	turbine::setApplicationData(*ctx, &studioCtx);
 | 
			
		||||
	StudioUI ui(ctx.get(), projectDataDir);
 | 
			
		||||
	StudioUI ui(*ctx, projectDataDir);
 | 
			
		||||
	studioCtx.ui = &ui;
 | 
			
		||||
	StudioUIDrawer drawer(ui);
 | 
			
		||||
	turbine::gl::addDrawer(*ctx, &drawer);
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,12 @@
 | 
			
		||||
 | 
			
		||||
namespace studio {
 | 
			
		||||
 | 
			
		||||
ox::Vector<const studio::Module*> modules;
 | 
			
		||||
static ox::Vector<const studio::Module*> modules;
 | 
			
		||||
 | 
			
		||||
void registerModule(studio::Module const*mod) noexcept {
 | 
			
		||||
	modules.emplace_back(mod);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct StudioConfig {
 | 
			
		||||
	static constexpr auto TypeName = "net.drinkingtea.studio.StudioConfig";
 | 
			
		||||
@@ -33,23 +38,25 @@ oxModelBegin(StudioConfig)
 | 
			
		||||
	oxModelFieldRename(show_project_explorer, showProjectExplorer)
 | 
			
		||||
oxModelEnd()
 | 
			
		||||
 | 
			
		||||
StudioUI::StudioUI(turbine::Context *ctx, ox::StringView projectDir) noexcept:
 | 
			
		||||
	m_ctx(*ctx),
 | 
			
		||||
	m_projectDir(projectDir),
 | 
			
		||||
	m_projectExplorer(ox::make_unique<ProjectExplorer>(m_ctx)),
 | 
			
		||||
	m_aboutPopup(*ctx) {
 | 
			
		||||
StudioUI::StudioUI(turbine::Context &ctx, ox::StringView projectDataDir) noexcept:
 | 
			
		||||
		m_ctx(ctx),
 | 
			
		||||
		m_projectDataDir(projectDataDir),
 | 
			
		||||
		m_projectExplorer(ox::make_unique<ProjectExplorer>(m_ctx)),
 | 
			
		||||
		m_aboutPopup(m_ctx) {
 | 
			
		||||
	m_projectExplorer->fileChosen.connect(this, &StudioUI::openFile);
 | 
			
		||||
	ImGui::GetIO().IniFilename = nullptr;
 | 
			
		||||
	loadModules();
 | 
			
		||||
	// open project and files
 | 
			
		||||
	const auto [config, err] = studio::readConfig<StudioConfig>(keelCtx(*ctx));
 | 
			
		||||
	auto const [config, err] = studio::readConfig<StudioConfig>(keelCtx(m_ctx));
 | 
			
		||||
	m_showProjectExplorer = config.showProjectExplorer;
 | 
			
		||||
	if (!err) {
 | 
			
		||||
		oxIgnoreError(openProject(config.projectPath));
 | 
			
		||||
		for (const auto &f : config.openFiles) {
 | 
			
		||||
			auto openFileErr = openFileActiveTab(f, config.activeTabItemName == f);
 | 
			
		||||
			if (openFileErr) {
 | 
			
		||||
				oxErrorf("\nCould not open editor for file:\n\t{}\nReason:\n\t{}\n", f, toStr(openFileErr));
 | 
			
		||||
		auto const openProjErr = openProject(config.projectPath);
 | 
			
		||||
		if (!openProjErr) {
 | 
			
		||||
			for (const auto &f: config.openFiles) {
 | 
			
		||||
				auto openFileErr = openFileActiveTab(f, config.activeTabItemName == f);
 | 
			
		||||
				if (openFileErr) {
 | 
			
		||||
					oxErrorf("\nCould not open editor for file:\n\t{}\nReason:\n\t{}\n", f, toStr(openFileErr));
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
@@ -83,7 +90,9 @@ void StudioUI::handleKeyEvent(turbine::Key key, bool down) noexcept {
 | 
			
		||||
				toggleProjectExplorer();
 | 
			
		||||
				break;
 | 
			
		||||
			case turbine::Key::Alpha_C:
 | 
			
		||||
				m_activeEditor->copy();
 | 
			
		||||
				if (m_activeEditor && m_activeEditor->copyEnabled()) {
 | 
			
		||||
					m_activeEditor->copy();
 | 
			
		||||
				}
 | 
			
		||||
				break;
 | 
			
		||||
			case turbine::Key::Alpha_N:
 | 
			
		||||
				m_newMenu.open();
 | 
			
		||||
@@ -98,10 +107,14 @@ void StudioUI::handleKeyEvent(turbine::Key key, bool down) noexcept {
 | 
			
		||||
				save();
 | 
			
		||||
				break;
 | 
			
		||||
			case turbine::Key::Alpha_V:
 | 
			
		||||
				m_activeEditor->paste();
 | 
			
		||||
				if (m_activeEditor && m_activeEditor->pasteEnabled()) {
 | 
			
		||||
					m_activeEditor->paste();
 | 
			
		||||
				}
 | 
			
		||||
				break;
 | 
			
		||||
			case turbine::Key::Alpha_X:
 | 
			
		||||
				m_activeEditor->cut();
 | 
			
		||||
				if (m_activeEditor && m_activeEditor->cutEnabled()) {
 | 
			
		||||
					m_activeEditor->cut();
 | 
			
		||||
				}
 | 
			
		||||
				break;
 | 
			
		||||
			case turbine::Key::Alpha_Y:
 | 
			
		||||
				redo();
 | 
			
		||||
@@ -169,19 +182,19 @@ void StudioUI::drawMenu() noexcept {
 | 
			
		||||
		if (ImGui::BeginMenu("Edit")) {
 | 
			
		||||
			auto undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr;
 | 
			
		||||
			if (ImGui::MenuItem("Undo", "Ctrl+Z", false, undoStack && undoStack->canUndo())) {
 | 
			
		||||
				 m_activeEditor->undoStack()->undo();
 | 
			
		||||
				undoStack->undo();
 | 
			
		||||
			}
 | 
			
		||||
			if (ImGui::MenuItem("Redo", "Ctrl+Y", false, undoStack && undoStack->canRedo())) {
 | 
			
		||||
				 m_activeEditor->undoStack()->redo();
 | 
			
		||||
				undoStack->redo();
 | 
			
		||||
			}
 | 
			
		||||
			ImGui::Separator();
 | 
			
		||||
			if (ImGui::MenuItem("Copy", "Ctrl+C")) {
 | 
			
		||||
			if (ImGui::MenuItem("Copy", "Ctrl+C", false, m_activeEditor && m_activeEditor->copyEnabled())) {
 | 
			
		||||
				m_activeEditor->copy();
 | 
			
		||||
			}
 | 
			
		||||
			if (ImGui::MenuItem("Cut", "Ctrl+X")) {
 | 
			
		||||
			if (ImGui::MenuItem("Cut", "Ctrl+X", false, m_activeEditor && m_activeEditor->cutEnabled())) {
 | 
			
		||||
				m_activeEditor->cut();
 | 
			
		||||
			}
 | 
			
		||||
			if (ImGui::MenuItem("Paste", "Ctrl+V")) {
 | 
			
		||||
			if (ImGui::MenuItem("Paste", "Ctrl+V", false, m_activeEditor && m_activeEditor->pasteEnabled()) && m_activeEditor) {
 | 
			
		||||
				m_activeEditor->paste();
 | 
			
		||||
			}
 | 
			
		||||
			ImGui::EndMenu();
 | 
			
		||||
@@ -240,6 +253,9 @@ void StudioUI::drawTabs() noexcept {
 | 
			
		||||
		}
 | 
			
		||||
		if (!open) {
 | 
			
		||||
			e->close();
 | 
			
		||||
			if (m_activeEditor == (*it).get()) {
 | 
			
		||||
				m_activeEditor = nullptr;
 | 
			
		||||
			}
 | 
			
		||||
			try {
 | 
			
		||||
				oxThrowError(m_editors.erase(it).moveTo(&it));
 | 
			
		||||
			} catch (const ox::Exception &ex) {
 | 
			
		||||
@@ -305,7 +321,7 @@ ox::Error StudioUI::openProject(ox::CRStringView path) noexcept {
 | 
			
		||||
	oxRequireM(fs, keel::loadRomFs(path));
 | 
			
		||||
	oxReturnError(keel::setRomFs(keelCtx(m_ctx), std::move(fs)));
 | 
			
		||||
	turbine::setWindowTitle(m_ctx, ox::sfmt("{} - {}", keelCtx(m_ctx).appName, path));
 | 
			
		||||
	m_project = ox::make_unique<studio::Project>(keelCtx(m_ctx), ox::String(path), m_projectDir);
 | 
			
		||||
	m_project = ox::make_unique<studio::Project>(keelCtx(m_ctx), ox::String(path), m_projectDataDir);
 | 
			
		||||
	auto sctx = applicationData<studio::StudioContext>(m_ctx);
 | 
			
		||||
	sctx->project = m_project.get();
 | 
			
		||||
	m_project->fileAdded.connect(m_projectExplorer.get(), &ProjectExplorer::refreshProjectTreeModel);
 | 
			
		||||
@@ -324,6 +340,9 @@ ox::Error StudioUI::openFile(ox::CRStringView path) noexcept {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ox::Error StudioUI::openFileActiveTab(ox::CRStringView path, bool makeActiveTab) noexcept {
 | 
			
		||||
	if (!m_project) {
 | 
			
		||||
		return OxError(1, "No project open to open a file from");
 | 
			
		||||
	}
 | 
			
		||||
	if (m_openFiles.contains(path)) {
 | 
			
		||||
		for (auto &e : m_editors) {
 | 
			
		||||
			if (makeActiveTab && e->itemPath() == path) {
 | 
			
		||||
@@ -332,7 +351,7 @@ ox::Error StudioUI::openFileActiveTab(ox::CRStringView path, bool makeActiveTab)
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return OxError(0);
 | 
			
		||||
		return {};
 | 
			
		||||
	}
 | 
			
		||||
	oxRequire(ext, studio::fileExt(path).to<ox::String>([](auto const&v) {return ox::String(v);}));
 | 
			
		||||
	// create Editor
 | 
			
		||||
@@ -372,18 +391,14 @@ ox::Error StudioUI::openFileActiveTab(ox::CRStringView path, bool makeActiveTab)
 | 
			
		||||
 | 
			
		||||
ox::Error StudioUI::closeFile(ox::CRStringView path) noexcept {
 | 
			
		||||
	if (!m_openFiles.contains(path)) {
 | 
			
		||||
		return OxError(0);
 | 
			
		||||
		return {};
 | 
			
		||||
	}
 | 
			
		||||
	oxIgnoreError(m_openFiles.erase(std::remove(m_openFiles.begin(), m_openFiles.end(), path)));
 | 
			
		||||
	// save to config
 | 
			
		||||
	studio::editConfig<StudioConfig>(keelCtx(m_ctx), [&](StudioConfig *config) {
 | 
			
		||||
		oxIgnoreError(config->openFiles.erase(std::remove(config->openFiles.begin(), config->openFiles.end(), path)));
 | 
			
		||||
	});
 | 
			
		||||
	return OxError(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void registerModule(const studio::Module *mod) noexcept {
 | 
			
		||||
	modules.emplace_back(mod);
 | 
			
		||||
	return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -24,27 +24,27 @@ class StudioUI: public ox::SignalHandler {
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		turbine::Context &m_ctx;
 | 
			
		||||
		ox::String m_projectDir;
 | 
			
		||||
		ox::UniquePtr<studio::Project> m_project;
 | 
			
		||||
		ox::String m_projectDataDir;
 | 
			
		||||
		ox::UPtr<studio::Project> m_project;
 | 
			
		||||
		studio::TaskRunner m_taskRunner;
 | 
			
		||||
		ox::Vector<ox::UniquePtr<studio::BaseEditor>> m_editors;
 | 
			
		||||
		ox::Vector<ox::UniquePtr<studio::Widget>> m_widgets;
 | 
			
		||||
		ox::Vector<ox::UPtr<studio::BaseEditor>> m_editors;
 | 
			
		||||
		ox::Vector<ox::UPtr<studio::Widget>> m_widgets;
 | 
			
		||||
		ox::HashMap<ox::String, studio::EditorMaker::Func> m_editorMakers;
 | 
			
		||||
		ox::UniquePtr<ProjectExplorer> m_projectExplorer;
 | 
			
		||||
		ox::UPtr<ProjectExplorer> m_projectExplorer;
 | 
			
		||||
		ox::Vector<ox::String> m_openFiles;
 | 
			
		||||
		studio::BaseEditor *m_activeEditorOnLastDraw = nullptr;
 | 
			
		||||
		studio::BaseEditor *m_activeEditor = nullptr;
 | 
			
		||||
		studio::BaseEditor *m_activeEditorUpdatePending = nullptr;
 | 
			
		||||
		NewMenu m_newMenu;
 | 
			
		||||
		AboutPopup m_aboutPopup;
 | 
			
		||||
		const ox::Array<studio::Popup*, 2> m_popups = {
 | 
			
		||||
		ox::Array<studio::Popup*, 2> const m_popups = {
 | 
			
		||||
			&m_newMenu,
 | 
			
		||||
			&m_aboutPopup
 | 
			
		||||
		};
 | 
			
		||||
		bool m_showProjectExplorer = true;
 | 
			
		||||
 | 
			
		||||
	public:
 | 
			
		||||
		explicit StudioUI(turbine::Context *ctx, ox::StringView projectDir) noexcept;
 | 
			
		||||
		explicit StudioUI(turbine::Context &ctx, ox::StringView projectDataDir) noexcept;
 | 
			
		||||
 | 
			
		||||
		void update() noexcept;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -76,17 +76,17 @@ class BaseEditor: public Widget {
 | 
			
		||||
		void setCutEnabled(bool);
 | 
			
		||||
 | 
			
		||||
		[[nodiscard]]
 | 
			
		||||
		bool cutEnabled() const;
 | 
			
		||||
		bool cutEnabled() const noexcept;
 | 
			
		||||
 | 
			
		||||
		void setCopyEnabled(bool);
 | 
			
		||||
 | 
			
		||||
		[[nodiscard]]
 | 
			
		||||
		bool copyEnabled() const;
 | 
			
		||||
		bool copyEnabled() const noexcept;
 | 
			
		||||
 | 
			
		||||
		void setPasteEnabled(bool);
 | 
			
		||||
 | 
			
		||||
		[[nodiscard]]
 | 
			
		||||
		bool pasteEnabled() const;
 | 
			
		||||
		bool pasteEnabled() const noexcept;
 | 
			
		||||
 | 
			
		||||
	protected:
 | 
			
		||||
		/**
 | 
			
		||||
 
 | 
			
		||||
@@ -54,8 +54,8 @@ void BaseEditor::save() noexcept {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BaseEditor::setUnsavedChanges(bool uc) {
 | 
			
		||||
    m_unsavedChanges = uc;
 | 
			
		||||
    unsavedChangesChanged.emit(uc);
 | 
			
		||||
	m_unsavedChanges = uc;
 | 
			
		||||
	unsavedChangesChanged.emit(uc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BaseEditor::unsavedChanges() const noexcept {
 | 
			
		||||
@@ -76,7 +76,7 @@ void BaseEditor::setCutEnabled(bool v) {
 | 
			
		||||
	cutEnabledChanged.emit(v);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BaseEditor::cutEnabled() const {
 | 
			
		||||
bool BaseEditor::cutEnabled() const noexcept {
 | 
			
		||||
	return m_cutEnabled;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -85,7 +85,7 @@ void BaseEditor::setCopyEnabled(bool v) {
 | 
			
		||||
	copyEnabledChanged.emit(v);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BaseEditor::copyEnabled() const {
 | 
			
		||||
bool BaseEditor::copyEnabled() const noexcept {
 | 
			
		||||
	return m_copyEnabled;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -94,12 +94,12 @@ void BaseEditor::setPasteEnabled(bool v) {
 | 
			
		||||
	pasteEnabledChanged.emit(v);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BaseEditor::pasteEnabled() const {
 | 
			
		||||
bool BaseEditor::pasteEnabled() const noexcept {
 | 
			
		||||
	return m_pasteEnabled;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ox::Error BaseEditor::saveItem() noexcept {
 | 
			
		||||
	return OxError(0);
 | 
			
		||||
	return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
UndoStack *BaseEditor::undoStack() noexcept {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user