[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,
|
||||
size_t lyr,
|
||||
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);
|
||||
return ws.map[lyr].tiles[idx];
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ target_sources(
|
||||
commands/modifytiles.cpp
|
||||
commands/editsize.cpp
|
||||
objectexplorer.cpp
|
||||
tileclipboard.cpp
|
||||
worldeditor-imgui.cpp
|
||||
worldeditorview.cpp
|
||||
)
|
||||
|
@ -37,4 +37,6 @@ class ModifyTilesCommand: public studio::UndoCommand {
|
||||
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/editsize.hpp"
|
||||
#include "commands/modifytiles.hpp"
|
||||
#include "tileclipboard.hpp"
|
||||
|
||||
#include "worldeditor-imgui.hpp"
|
||||
|
||||
@ -113,6 +114,69 @@ void WorldEditorImGui::onActivated() noexcept {
|
||||
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 {
|
||||
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] {
|
||||
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();
|
||||
if (io.MouseDown[0]) {
|
||||
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()) {
|
||||
m_selTracker.finishSelection();
|
||||
m_selection.emplace(m_selTracker.selection());
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -309,8 +380,9 @@ void WorldEditorImGui::rmObjSet() noexcept {
|
||||
}
|
||||
|
||||
ox::Error WorldEditorImGui::handleDepUpdate(ox::StringView, ox::UUID const&uuid) noexcept {
|
||||
auto const objSetMatches = [&uuid](ObjectSetEntry const&set) {
|
||||
auto const [setUuid, err] = keel::uuidUrlToUuid(set.path);
|
||||
auto &kctx = keelCtx(m_sctx.tctx);
|
||||
auto const objSetMatches = [&uuid, &kctx](ObjectSetEntry const&set) {
|
||||
auto const [setUuid, err] = keel::getUuid(kctx, set.path);
|
||||
return !err && setUuid == uuid;
|
||||
};
|
||||
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 {
|
||||
setCopyEnabled(false);
|
||||
setCutEnabled(false);
|
||||
setPasteEnabled(false);
|
||||
m_selection.reset();
|
||||
m_view.clearSelection();
|
||||
}
|
||||
|
@ -58,6 +58,12 @@ class WorldEditorImGui: public studio::Editor {
|
||||
void onActivated() noexcept override;
|
||||
|
||||
protected:
|
||||
void copy() noexcept override;
|
||||
|
||||
void cut() noexcept override;
|
||||
|
||||
void paste() override;
|
||||
|
||||
ox::Error saveItem() noexcept final;
|
||||
|
||||
private:
|
||||
@ -73,7 +79,7 @@ class WorldEditorImGui: public studio::Editor {
|
||||
|
||||
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;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user