[jasper/world/studio] Add copy/cut/paste to WorldEditor
All checks were successful
Build / build (push) Successful in 3m18s
All checks were successful
Build / build (push) Successful in 3m18s
This commit is contained in:
parent
8ae716e09c
commit
db5cff5e8c
@ -114,7 +114,7 @@ auto &tile(
|
|||||||
ox::CommonRefWith<WorldStatic> auto&ws,
|
ox::CommonRefWith<WorldStatic> auto&ws,
|
||||||
size_t lyr,
|
size_t lyr,
|
||||||
ox::IntegerRange_c<500> auto col,
|
ox::IntegerRange_c<500> auto col,
|
||||||
ox::Integer_c auto row) noexcept {
|
ox::IntegerRange_c<500> auto row) noexcept {
|
||||||
auto const idx = static_cast<size_t>(row) * static_cast<size_t>(ws.columns) + static_cast<size_t>(col);
|
auto const idx = static_cast<size_t>(row) * static_cast<size_t>(ws.columns) + static_cast<size_t>(col);
|
||||||
return ws.map[lyr].tiles[idx];
|
return ws.map[lyr].tiles[idx];
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ target_sources(
|
|||||||
commands/modifytiles.cpp
|
commands/modifytiles.cpp
|
||||||
commands/editsize.cpp
|
commands/editsize.cpp
|
||||||
objectexplorer.cpp
|
objectexplorer.cpp
|
||||||
|
tileclipboard.cpp
|
||||||
worldeditor-imgui.cpp
|
worldeditor-imgui.cpp
|
||||||
worldeditorview.cpp
|
worldeditorview.cpp
|
||||||
)
|
)
|
||||||
|
@ -37,4 +37,6 @@ class ModifyTilesCommand: public studio::UndoCommand {
|
|||||||
void swap() noexcept;
|
void swap() noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using TileUpdate = ModifyTilesCommand::Mod;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "tileclipboard.hpp"
|
||||||
|
|
||||||
|
namespace jasper::world {
|
||||||
|
|
||||||
|
TileClipboard::TileClipboard(ox::Size selSz) noexcept: m_selSz(selSz) {}
|
||||||
|
|
||||||
|
ox::Size TileClipboard::selectionSize() const noexcept {
|
||||||
|
return m_selSz;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TileClipboard::addTile(const ModifyTilesCommand::Mod&mod) noexcept {
|
||||||
|
m_tileMods.emplace_back(mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
ox::SpanView<ModifyTilesCommand::Mod> TileClipboard::tiles() const noexcept {
|
||||||
|
return m_tileMods;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <turbine/turbine.hpp>
|
||||||
|
|
||||||
|
#include "commands/modifytiles.hpp"
|
||||||
|
|
||||||
|
namespace jasper::world {
|
||||||
|
|
||||||
|
class TileClipboard : public turbine::ClipboardObject<TileClipboard> {
|
||||||
|
public:
|
||||||
|
static constexpr auto TypeName = "net.drinkingtea.jasper.world.studio.TileClipboard";
|
||||||
|
static constexpr auto TypeVersion = 1;
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ox::Size m_selSz;
|
||||||
|
ox::Vector<ModifyTilesCommand::Mod> m_tileMods;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit TileClipboard(ox::Size selSz) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
ox::Size selectionSize() const noexcept;
|
||||||
|
|
||||||
|
void addTile(ModifyTilesCommand::Mod const&mod) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
ox::SpanView<ModifyTilesCommand::Mod> tiles() const noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -14,6 +14,7 @@
|
|||||||
#include "commands/addrmobjectset.hpp"
|
#include "commands/addrmobjectset.hpp"
|
||||||
#include "commands/editsize.hpp"
|
#include "commands/editsize.hpp"
|
||||||
#include "commands/modifytiles.hpp"
|
#include "commands/modifytiles.hpp"
|
||||||
|
#include "tileclipboard.hpp"
|
||||||
|
|
||||||
#include "worldeditor-imgui.hpp"
|
#include "worldeditor-imgui.hpp"
|
||||||
|
|
||||||
@ -113,6 +114,69 @@ void WorldEditorImGui::onActivated() noexcept {
|
|||||||
oxLogError(m_view.setupWorld());
|
oxLogError(m_view.setupWorld());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WorldEditorImGui::copy() noexcept {
|
||||||
|
if (m_selection) {
|
||||||
|
auto mods = ox::make_unique<TileClipboard>(m_selection->size());
|
||||||
|
studio::iterateSelection(*m_selection, [this, &mods](int32_t x, int32_t y) {
|
||||||
|
auto &t = tile(m_doc, m_activeLayer, x, y);
|
||||||
|
x -= m_selection->a.x;
|
||||||
|
y -= m_selection->a.y;
|
||||||
|
mods->addTile({
|
||||||
|
.layer = m_activeLayer,
|
||||||
|
.tileAddr = {x, y},
|
||||||
|
.objId = t.obj.worldObjectId,
|
||||||
|
.setId = t.obj.worldObjectSetId,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
setClipboardObject(m_sctx.tctx, std::move(mods));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldEditorImGui::cut() noexcept {
|
||||||
|
if (m_selection) {
|
||||||
|
copy();
|
||||||
|
ox::Vector<ModifyTilesCommand::Mod> mods;
|
||||||
|
studio::iterateSelection(*m_selection, [this, &mods](int32_t x, int32_t y) {
|
||||||
|
mods.emplace_back(ModifyTilesCommand::Mod{
|
||||||
|
.layer = m_activeLayer,
|
||||||
|
.tileAddr = {x, y},
|
||||||
|
.objId = 0,
|
||||||
|
.setId = 0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
std::ignore = pushCommand<ModifyTilesCommand>(
|
||||||
|
m_doc,
|
||||||
|
m_worldStatic,
|
||||||
|
m_objCache,
|
||||||
|
std::move(mods));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldEditorImGui::paste() {
|
||||||
|
if (m_selection) {
|
||||||
|
auto const cb = getClipboardObject<TileClipboard>(m_sctx.tctx).unwrapThrow();
|
||||||
|
auto const selSz = cb->selectionSize();
|
||||||
|
auto &sel = *m_selection;
|
||||||
|
sel.b.x = ox::min(sel.a.x + selSz.width, m_doc.columns - 1);
|
||||||
|
sel.b.y = ox::min(sel.a.y + selSz.height, m_doc.rows - 1);
|
||||||
|
m_view.setSelection(sel);
|
||||||
|
ox::Vector<ModifyTilesCommand::Mod> mods;
|
||||||
|
for (auto const&m : cb->tiles()) {
|
||||||
|
mods.emplace_back(ModifyTilesCommand::Mod{
|
||||||
|
.layer = m_activeLayer,
|
||||||
|
.tileAddr = m.tileAddr + sel.a,
|
||||||
|
.objId = m.objId,
|
||||||
|
.setId = m.setId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
std::ignore = pushCommand<ModifyTilesCommand>(
|
||||||
|
m_doc,
|
||||||
|
m_worldStatic,
|
||||||
|
m_objCache,
|
||||||
|
std::move(mods));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ox::Error WorldEditorImGui::saveItem() noexcept {
|
ox::Error WorldEditorImGui::saveItem() noexcept {
|
||||||
return m_sctx.project->writeObj(itemPath(), m_doc, ox::ClawFormat::Organic);
|
return m_sctx.project->writeObj(itemPath(), m_doc, ox::ClawFormat::Organic);
|
||||||
}
|
}
|
||||||
@ -228,10 +292,22 @@ void WorldEditorImGui::drawWorldView() noexcept {
|
|||||||
std::ignore = ig::dragDropTarget([this, fbPaneScale] {
|
std::ignore = ig::dragDropTarget([this, fbPaneScale] {
|
||||||
return handleDrop(fbPaneScale);
|
return handleDrop(fbPaneScale);
|
||||||
});
|
});
|
||||||
handleSelection(static_cast<ox::Size>(ox::Vec2(paneSize)), fbPaneScale);
|
handleMouseSelection(static_cast<ox::Size>(ox::Vec2(paneSize)), fbPaneScale);
|
||||||
|
auto const&io = ImGui::GetIO();
|
||||||
|
if (io.KeyCtrl) {
|
||||||
|
if (io.KeysDown[ImGuiKey_G]) {
|
||||||
|
clearSelection();
|
||||||
|
} else if (io.KeysDown[ImGuiKey_A]) {
|
||||||
|
m_selection.emplace(ox::Point{0, 0}, ox::Point{m_doc.columns - 1, m_doc.rows - 1});
|
||||||
|
m_view.setSelection(*m_selection);
|
||||||
|
setCopyEnabled(true);
|
||||||
|
setCutEnabled(true);
|
||||||
|
setPasteEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldEditorImGui::handleSelection(ox::Size const&paneSz, float fbPaneScale) noexcept {
|
void WorldEditorImGui::handleMouseSelection(ox::Size const&paneSz, float fbPaneScale) noexcept {
|
||||||
auto const&io = ImGui::GetIO();
|
auto const&io = ImGui::GetIO();
|
||||||
if (io.MouseDown[0]) {
|
if (io.MouseDown[0]) {
|
||||||
auto const fbPos = world::fbPos(ox::Vec2{io.MousePos});
|
auto const fbPos = world::fbPos(ox::Vec2{io.MousePos});
|
||||||
@ -246,14 +322,9 @@ void WorldEditorImGui::handleSelection(ox::Size const&paneSz, float fbPaneScale)
|
|||||||
} else if (io.MouseReleased[0] && m_selTracker.selectionOngoing()) {
|
} else if (io.MouseReleased[0] && m_selTracker.selectionOngoing()) {
|
||||||
m_selTracker.finishSelection();
|
m_selTracker.finishSelection();
|
||||||
m_selection.emplace(m_selTracker.selection());
|
m_selection.emplace(m_selTracker.selection());
|
||||||
}
|
setCopyEnabled(true);
|
||||||
if (io.KeyCtrl) {
|
setCutEnabled(true);
|
||||||
if (io.KeysDown[ImGuiKey_G]) {
|
setPasteEnabled(true);
|
||||||
clearSelection();
|
|
||||||
} else if (io.KeysDown[ImGuiKey_A]) {
|
|
||||||
m_selection.emplace(ox::Point{0, 0}, ox::Point{m_doc.columns - 1, m_doc.rows - 1});
|
|
||||||
m_view.setSelection(*m_selection);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,8 +380,9 @@ void WorldEditorImGui::rmObjSet() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ox::Error WorldEditorImGui::handleDepUpdate(ox::StringView, ox::UUID const&uuid) noexcept {
|
ox::Error WorldEditorImGui::handleDepUpdate(ox::StringView, ox::UUID const&uuid) noexcept {
|
||||||
auto const objSetMatches = [&uuid](ObjectSetEntry const&set) {
|
auto &kctx = keelCtx(m_sctx.tctx);
|
||||||
auto const [setUuid, err] = keel::uuidUrlToUuid(set.path);
|
auto const objSetMatches = [&uuid, &kctx](ObjectSetEntry const&set) {
|
||||||
|
auto const [setUuid, err] = keel::getUuid(kctx, set.path);
|
||||||
return !err && setUuid == uuid;
|
return !err && setUuid == uuid;
|
||||||
};
|
};
|
||||||
auto const depMatches = [&uuid](ox::SmallMap<ox::UUID, bool>::Pair const&set) {
|
auto const depMatches = [&uuid](ox::SmallMap<ox::UUID, bool>::Pair const&set) {
|
||||||
@ -355,6 +427,9 @@ bool WorldEditorImGui::tileSelected(ox::Point const&pt) const noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WorldEditorImGui::clearSelection() noexcept {
|
void WorldEditorImGui::clearSelection() noexcept {
|
||||||
|
setCopyEnabled(false);
|
||||||
|
setCutEnabled(false);
|
||||||
|
setPasteEnabled(false);
|
||||||
m_selection.reset();
|
m_selection.reset();
|
||||||
m_view.clearSelection();
|
m_view.clearSelection();
|
||||||
}
|
}
|
||||||
|
@ -58,6 +58,12 @@ class WorldEditorImGui: public studio::Editor {
|
|||||||
void onActivated() noexcept override;
|
void onActivated() noexcept override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void copy() noexcept override;
|
||||||
|
|
||||||
|
void cut() noexcept override;
|
||||||
|
|
||||||
|
void paste() override;
|
||||||
|
|
||||||
ox::Error saveItem() noexcept final;
|
ox::Error saveItem() noexcept final;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -73,7 +79,7 @@ class WorldEditorImGui: public studio::Editor {
|
|||||||
|
|
||||||
void drawWorldView() noexcept;
|
void drawWorldView() noexcept;
|
||||||
|
|
||||||
void handleSelection(ox::Size const&paneSz, float fbPaneScale) noexcept;
|
void handleMouseSelection(ox::Size const&paneSz, float fbPaneScale) noexcept;
|
||||||
|
|
||||||
ox::Error handleDrop(float fbPaneScale) noexcept;
|
ox::Error handleDrop(float fbPaneScale) noexcept;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user