From 339741fe358285f9580ccd93caae62485cea9747 Mon Sep 17 00:00:00 2001 From: scawful Date: Sat, 13 Sep 2025 10:59:57 -0400 Subject: [PATCH] Add clipboard functionality for overworld tile16 selection in OverworldEditor - Introduced SharedClipboard struct in EditorContext to manage cross-session clipboard data. - Implemented Copy and Paste methods in OverworldEditor to handle tile16 selections and transfers. - Enhanced clipboard management to support both rectangular selections and single tile copies. - Added error handling for clipboard operations to ensure robust functionality. --- src/app/editor/editor.h | 16 ++++ src/app/editor/overworld/overworld_editor.cc | 90 ++++++++++++++++++++ src/app/editor/overworld/overworld_editor.h | 4 +- 3 files changed, 108 insertions(+), 2 deletions(-) diff --git a/src/app/editor/editor.h b/src/app/editor/editor.h index 8eebbb6c..211b904f 100644 --- a/src/app/editor/editor.h +++ b/src/app/editor/editor.h @@ -2,6 +2,7 @@ #define YAZE_APP_CORE_EDITOR_H #include +#include #include "absl/status/status.h" #include "app/editor/system/command_manager.h" @@ -24,6 +25,21 @@ struct EditorContext { HistoryManager history_manager; PopupManager* popup_manager = nullptr; ShortcutManager shortcut_manager; + // Cross-session shared clipboard for editor data transfers + struct SharedClipboard { + // Overworld tile16 selection payload + bool has_overworld_tile16 = false; + std::vector overworld_tile16_ids; + int overworld_width = 0; // in tile16 units + int overworld_height = 0; // in tile16 units + + void Clear() { + has_overworld_tile16 = false; + overworld_tile16_ids.clear(); + overworld_width = 0; + overworld_height = 0; + } + } shared_clipboard; }; enum class EditorType { diff --git a/src/app/editor/overworld/overworld_editor.cc b/src/app/editor/overworld/overworld_editor.cc index 55edb4bf..31e41791 100644 --- a/src/app/editor/overworld/overworld_editor.cc +++ b/src/app/editor/overworld/overworld_editor.cc @@ -567,6 +567,96 @@ void OverworldEditor::CheckForSelectRectangle() { ow_map_canvas_.DrawBitmapGroup(tile16_ids, tile16_blockset_, 0x10); } +absl::Status OverworldEditor::Copy() { + if (!context_) return absl::FailedPreconditionError("No editor context"); + // If a rectangle selection exists, copy its tile16 IDs into shared clipboard + if (ow_map_canvas_.select_rect_active() && + !ow_map_canvas_.selected_tiles().empty()) { + std::vector ids; + ids.reserve(ow_map_canvas_.selected_tiles().size()); + for (const auto &pos : ow_map_canvas_.selected_tiles()) { + ids.push_back(overworld_.GetTileFromPosition(pos)); + } + // Determine width/height in tile16 based on selection bounds + const auto start = ow_map_canvas_.selected_points()[0]; + const auto end = ow_map_canvas_.selected_points()[1]; + const int start_x = static_cast(std::floor(std::min(start.x, end.x) / 16.0f)); + const int end_x = static_cast(std::floor(std::max(start.x, end.x) / 16.0f)); + const int start_y = static_cast(std::floor(std::min(start.y, end.y) / 16.0f)); + const int end_y = static_cast(std::floor(std::max(start.y, end.y) / 16.0f)); + const int width = end_x - start_x + 1; + const int height = end_y - start_y + 1; + + context_->shared_clipboard.overworld_tile16_ids = std::move(ids); + context_->shared_clipboard.overworld_width = width; + context_->shared_clipboard.overworld_height = height; + context_->shared_clipboard.has_overworld_tile16 = true; + return absl::OkStatus(); + } + // Single tile copy fallback + if (current_tile16_ >= 0) { + context_->shared_clipboard.overworld_tile16_ids = {current_tile16_}; + context_->shared_clipboard.overworld_width = 1; + context_->shared_clipboard.overworld_height = 1; + context_->shared_clipboard.has_overworld_tile16 = true; + return absl::OkStatus(); + } + return absl::FailedPreconditionError("Nothing selected to copy"); +} + +absl::Status OverworldEditor::Paste() { + if (!context_) return absl::FailedPreconditionError("No editor context"); + if (!context_->shared_clipboard.has_overworld_tile16) { + return absl::FailedPreconditionError("Clipboard empty"); + } + if (ow_map_canvas_.points().empty() && + ow_map_canvas_.selected_tile_pos().x == -1) { + return absl::FailedPreconditionError("No paste target"); + } + + // Determine paste anchor position (use current mouse drawn tile position) + const ImVec2 anchor = ow_map_canvas_.drawn_tile_position(); + + // Compute anchor in tile16 grid within the current map + const int tile16_x = (static_cast(anchor.x) % kOverworldMapSize) / kTile16Size; + const int tile16_y = (static_cast(anchor.y) % kOverworldMapSize) / kTile16Size; + + auto &selected_world = + (current_world_ == 0) ? overworld_.mutable_map_tiles()->light_world + : (current_world_ == 1) ? overworld_.mutable_map_tiles()->dark_world + : overworld_.mutable_map_tiles()->special_world; + + const int superY = current_map_ / 8; + const int superX = current_map_ % 8; + const int tiles_per_local_map = 512 / kTile16Size; + + const int width = context_->shared_clipboard.overworld_width; + const int height = context_->shared_clipboard.overworld_height; + const auto &ids = context_->shared_clipboard.overworld_tile16_ids; + + // Guard + if (width * height != static_cast(ids.size())) { + return absl::InternalError("Clipboard dimensions mismatch"); + } + + for (int dy = 0; dy < height; ++dy) { + for (int dx = 0; dx < width; ++dx) { + const int id = ids[dy * width + dx]; + const int gx = tile16_x + dx; + const int gy = tile16_y + dy; + + const int global_x = superX * 32 + gx; + const int global_y = superY * 32 + gy; + if (global_x < 0 || global_x >= 256 || global_y < 0 || global_y >= 256) + continue; + selected_world[global_x][global_y] = id; + } + } + + RefreshOverworldMap(); + return absl::OkStatus(); +} + absl::Status OverworldEditor::CheckForCurrentMap() { // 4096x4096, 512x512 maps and some are larges maps 1024x1024 const auto mouse_position = ImGui::GetIO().MousePos; diff --git a/src/app/editor/overworld/overworld_editor.h b/src/app/editor/overworld/overworld_editor.h index 7e2659e1..12b931f0 100644 --- a/src/app/editor/overworld/overworld_editor.h +++ b/src/app/editor/overworld/overworld_editor.h @@ -85,8 +85,8 @@ class OverworldEditor : public Editor, public gfx::GfxContext { absl::Status Undo() override { return absl::UnimplementedError("Undo"); } absl::Status Redo() override { return absl::UnimplementedError("Redo"); } absl::Status Cut() override { return absl::UnimplementedError("Cut"); } - absl::Status Copy() override { return absl::UnimplementedError("Copy"); } - absl::Status Paste() override { return absl::UnimplementedError("Paste"); } + absl::Status Copy() override; + absl::Status Paste() override; absl::Status Find() override { return absl::UnimplementedError("Find"); } absl::Status Save() override; absl::Status Clear() override;