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
|
#define YAZE_APP_CORE_EDITOR_H
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "absl/status/status.h"
|
#include "absl/status/status.h"
|
||||||
#include "app/editor/system/command_manager.h"
|
#include "app/editor/system/command_manager.h"
|
||||||
@@ -24,6 +25,21 @@ struct EditorContext {
|
|||||||
HistoryManager history_manager;
|
HistoryManager history_manager;
|
||||||
PopupManager* popup_manager = nullptr;
|
PopupManager* popup_manager = nullptr;
|
||||||
ShortcutManager shortcut_manager;
|
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 {
|
enum class EditorType {
|
||||||
|
|||||||
@@ -567,6 +567,96 @@ void OverworldEditor::CheckForSelectRectangle() {
|
|||||||
ow_map_canvas_.DrawBitmapGroup(tile16_ids, tile16_blockset_, 0x10);
|
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() {
|
absl::Status OverworldEditor::CheckForCurrentMap() {
|
||||||
// 4096x4096, 512x512 maps and some are larges maps 1024x1024
|
// 4096x4096, 512x512 maps and some are larges maps 1024x1024
|
||||||
const auto mouse_position = ImGui::GetIO().MousePos;
|
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 Undo() override { return absl::UnimplementedError("Undo"); }
|
||||||
absl::Status Redo() override { return absl::UnimplementedError("Redo"); }
|
absl::Status Redo() override { return absl::UnimplementedError("Redo"); }
|
||||||
absl::Status Cut() override { return absl::UnimplementedError("Cut"); }
|
absl::Status Cut() override { return absl::UnimplementedError("Cut"); }
|
||||||
absl::Status Copy() override { return absl::UnimplementedError("Copy"); }
|
absl::Status Copy() override;
|
||||||
absl::Status Paste() override { return absl::UnimplementedError("Paste"); }
|
absl::Status Paste() override;
|
||||||
absl::Status Find() override { return absl::UnimplementedError("Find"); }
|
absl::Status Find() override { return absl::UnimplementedError("Find"); }
|
||||||
absl::Status Save() override;
|
absl::Status Save() override;
|
||||||
absl::Status Clear() override;
|
absl::Status Clear() override;
|
||||||
|
|||||||
Reference in New Issue
Block a user