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.
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
#define YAZE_APP_CORE_EDITOR_H
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
#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<int> 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 {
|
||||
|
||||
@@ -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<int> 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<int>(std::floor(std::min(start.x, end.x) / 16.0f));
|
||||
const int end_x = static_cast<int>(std::floor(std::max(start.x, end.x) / 16.0f));
|
||||
const int start_y = static_cast<int>(std::floor(std::min(start.y, end.y) / 16.0f));
|
||||
const int end_y = static_cast<int>(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<int>(anchor.x) % kOverworldMapSize) / kTile16Size;
|
||||
const int tile16_y = (static_cast<int>(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<int>(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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user