refactor: Enhance OverworldEditor with Dynamic Context Menu and Tile Selector Widget
- Replaced static context menu setup in OverworldEditor with dynamic configuration based on the current map state, improving usability and responsiveness. - Introduced TileSelectorWidget for better tile selection management, allowing for a more intuitive user experience when selecting tiles. - Updated canvas controls to include zoom in and zoom out functionalities, enhancing the editor's navigation capabilities. - Cleaned up legacy context menu code and improved overall organization for better maintainability and clarity.
This commit is contained in:
303
src/app/gui/canvas/canvas_automation_api.cc
Normal file
303
src/app/gui/canvas/canvas_automation_api.cc
Normal file
@@ -0,0 +1,303 @@
|
||||
#include "app/gui/canvas/canvas_automation_api.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include "app/gui/canvas.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
CanvasAutomationAPI::CanvasAutomationAPI(Canvas* canvas) : canvas_(canvas) {}
|
||||
|
||||
// ============================================================================
|
||||
// Tile Operations
|
||||
// ============================================================================
|
||||
|
||||
bool CanvasAutomationAPI::SetTileAt(int x, int y, int tile_id) {
|
||||
if (!IsInBounds(x, y)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tile_paint_callback_) {
|
||||
return tile_paint_callback_(x, y, tile_id);
|
||||
}
|
||||
|
||||
// Default behavior: add to canvas points for drawing
|
||||
// Note: Actual tile painting depends on the editor's canvas integration
|
||||
ImVec2 canvas_pos = TileToCanvas(x, y);
|
||||
canvas_->mutable_points()->push_back(canvas_pos);
|
||||
return true;
|
||||
}
|
||||
|
||||
int CanvasAutomationAPI::GetTileAt(int x, int y) const {
|
||||
if (!IsInBounds(x, y)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tile_query_callback_) {
|
||||
return tile_query_callback_(x, y);
|
||||
}
|
||||
|
||||
// Default: return -1 (requires callback for actual implementation)
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool CanvasAutomationAPI::SetTiles(
|
||||
const std::vector<std::tuple<int, int, int>>& tiles) {
|
||||
bool all_success = true;
|
||||
for (const auto& [x, y, tile_id] : tiles) {
|
||||
if (!SetTileAt(x, y, tile_id)) {
|
||||
all_success = false;
|
||||
}
|
||||
}
|
||||
return all_success;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Selection Operations
|
||||
// ============================================================================
|
||||
|
||||
void CanvasAutomationAPI::SelectTile(int x, int y) {
|
||||
if (!IsInBounds(x, y)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImVec2 canvas_pos = TileToCanvas(x, y);
|
||||
canvas_->mutable_selected_points()->clear();
|
||||
canvas_->mutable_selected_points()->push_back(canvas_pos);
|
||||
canvas_->mutable_selected_points()->push_back(canvas_pos);
|
||||
}
|
||||
|
||||
void CanvasAutomationAPI::SelectTileRect(int x1, int y1, int x2, int y2) {
|
||||
// Ensure x1 <= x2 and y1 <= y2
|
||||
if (x1 > x2) std::swap(x1, x2);
|
||||
if (y1 > y2) std::swap(y1, y2);
|
||||
|
||||
if (!IsInBounds(x1, y1) || !IsInBounds(x2, y2)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImVec2 start = TileToCanvas(x1, y1);
|
||||
ImVec2 end = TileToCanvas(x2, y2);
|
||||
|
||||
canvas_->mutable_selected_points()->clear();
|
||||
canvas_->mutable_selected_points()->push_back(start);
|
||||
canvas_->mutable_selected_points()->push_back(end);
|
||||
}
|
||||
|
||||
CanvasAutomationAPI::SelectionState CanvasAutomationAPI::GetSelection() const {
|
||||
SelectionState state;
|
||||
|
||||
const auto& selected_points = canvas_->selected_points();
|
||||
if (selected_points.size() >= 2) {
|
||||
state.has_selection = true;
|
||||
state.selection_start = selected_points[0];
|
||||
state.selection_end = selected_points[1];
|
||||
|
||||
// Convert canvas positions back to tile coordinates
|
||||
ImVec2 tile_start = CanvasToTile(state.selection_start);
|
||||
ImVec2 tile_end = CanvasToTile(state.selection_end);
|
||||
|
||||
// Ensure proper ordering
|
||||
int min_x = std::min(static_cast<int>(tile_start.x),
|
||||
static_cast<int>(tile_end.x));
|
||||
int max_x = std::max(static_cast<int>(tile_start.x),
|
||||
static_cast<int>(tile_end.x));
|
||||
int min_y = std::min(static_cast<int>(tile_start.y),
|
||||
static_cast<int>(tile_end.y));
|
||||
int max_y = std::max(static_cast<int>(tile_start.y),
|
||||
static_cast<int>(tile_end.y));
|
||||
|
||||
// Generate all tiles in selection rectangle
|
||||
for (int y = min_y; y <= max_y; ++y) {
|
||||
for (int x = min_x; x <= max_x; ++x) {
|
||||
state.selected_tiles.push_back(ImVec2(static_cast<float>(x),
|
||||
static_cast<float>(y)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
void CanvasAutomationAPI::ClearSelection() {
|
||||
canvas_->mutable_selected_points()->clear();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// View Operations
|
||||
// ============================================================================
|
||||
|
||||
void CanvasAutomationAPI::ScrollToTile(int x, int y, bool center) {
|
||||
if (!IsInBounds(x, y)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (center) {
|
||||
CenterOn(x, y);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if tile is already visible
|
||||
if (IsTileVisible(x, y)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Scroll to make tile visible
|
||||
ImVec2 tile_canvas_pos = TileToCanvas(x, y);
|
||||
|
||||
// Get current scroll and canvas size
|
||||
ImVec2 current_scroll = canvas_->scrolling();
|
||||
ImVec2 canvas_size = canvas_->canvas_size();
|
||||
|
||||
// Calculate new scroll to make tile visible at top-left
|
||||
float new_scroll_x = -tile_canvas_pos.x;
|
||||
float new_scroll_y = -tile_canvas_pos.y;
|
||||
|
||||
canvas_->set_scrolling(ImVec2(new_scroll_x, new_scroll_y));
|
||||
}
|
||||
|
||||
void CanvasAutomationAPI::CenterOn(int x, int y) {
|
||||
if (!IsInBounds(x, y)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImVec2 tile_canvas_pos = TileToCanvas(x, y);
|
||||
ImVec2 canvas_size = canvas_->canvas_size();
|
||||
|
||||
// Center the tile in the canvas view
|
||||
float new_scroll_x = -(tile_canvas_pos.x - canvas_size.x / 2.0f);
|
||||
float new_scroll_y = -(tile_canvas_pos.y - canvas_size.y / 2.0f);
|
||||
|
||||
canvas_->set_scrolling(ImVec2(new_scroll_x, new_scroll_y));
|
||||
}
|
||||
|
||||
void CanvasAutomationAPI::SetZoom(float zoom) {
|
||||
// Clamp zoom to reasonable range
|
||||
zoom = std::max(0.25f, std::min(zoom, 4.0f));
|
||||
canvas_->set_global_scale(zoom);
|
||||
}
|
||||
|
||||
float CanvasAutomationAPI::GetZoom() const {
|
||||
return canvas_->global_scale();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Query Operations
|
||||
// ============================================================================
|
||||
|
||||
CanvasAutomationAPI::Dimensions CanvasAutomationAPI::GetDimensions() const {
|
||||
Dimensions dims;
|
||||
|
||||
// Get canvas size in pixels
|
||||
ImVec2 canvas_size = canvas_->canvas_size();
|
||||
float scale = canvas_->global_scale();
|
||||
|
||||
// Determine tile size from canvas grid size
|
||||
int tile_size = 16; // Default
|
||||
switch (canvas_->grid_size()) {
|
||||
case CanvasGridSize::k8x8:
|
||||
tile_size = 8;
|
||||
break;
|
||||
case CanvasGridSize::k16x16:
|
||||
tile_size = 16;
|
||||
break;
|
||||
case CanvasGridSize::k32x32:
|
||||
tile_size = 32;
|
||||
break;
|
||||
case CanvasGridSize::k64x64:
|
||||
tile_size = 64;
|
||||
break;
|
||||
}
|
||||
|
||||
dims.tile_size = tile_size;
|
||||
dims.width_tiles = static_cast<int>(canvas_size.x / (tile_size * scale));
|
||||
dims.height_tiles = static_cast<int>(canvas_size.y / (tile_size * scale));
|
||||
|
||||
return dims;
|
||||
}
|
||||
|
||||
CanvasAutomationAPI::VisibleRegion CanvasAutomationAPI::GetVisibleRegion() const {
|
||||
VisibleRegion region;
|
||||
|
||||
ImVec2 scroll = canvas_->scrolling();
|
||||
ImVec2 canvas_size = canvas_->canvas_size();
|
||||
float scale = canvas_->global_scale();
|
||||
int tile_size = GetDimensions().tile_size;
|
||||
|
||||
// Top-left corner of visible region
|
||||
ImVec2 top_left = CanvasToTile(ImVec2(-scroll.x, -scroll.y));
|
||||
|
||||
// Bottom-right corner of visible region
|
||||
ImVec2 bottom_right = CanvasToTile(ImVec2(-scroll.x + canvas_size.x,
|
||||
-scroll.y + canvas_size.y));
|
||||
|
||||
region.min_x = std::max(0, static_cast<int>(top_left.x));
|
||||
region.min_y = std::max(0, static_cast<int>(top_left.y));
|
||||
|
||||
Dimensions dims = GetDimensions();
|
||||
region.max_x = std::min(dims.width_tiles - 1, static_cast<int>(bottom_right.x));
|
||||
region.max_y = std::min(dims.height_tiles - 1, static_cast<int>(bottom_right.y));
|
||||
|
||||
return region;
|
||||
}
|
||||
|
||||
bool CanvasAutomationAPI::IsTileVisible(int x, int y) const {
|
||||
if (!IsInBounds(x, y)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
VisibleRegion region = GetVisibleRegion();
|
||||
return x >= region.min_x && x <= region.max_x &&
|
||||
y >= region.min_y && y <= region.max_y;
|
||||
}
|
||||
|
||||
bool CanvasAutomationAPI::IsInBounds(int x, int y) const {
|
||||
if (x < 0 || y < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Dimensions dims = GetDimensions();
|
||||
return x < dims.width_tiles && y < dims.height_tiles;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Coordinate Conversion
|
||||
// ============================================================================
|
||||
|
||||
ImVec2 CanvasAutomationAPI::TileToCanvas(int x, int y) const {
|
||||
int tile_size = GetDimensions().tile_size;
|
||||
float scale = canvas_->global_scale();
|
||||
|
||||
float canvas_x = x * tile_size * scale;
|
||||
float canvas_y = y * tile_size * scale;
|
||||
|
||||
return ImVec2(canvas_x, canvas_y);
|
||||
}
|
||||
|
||||
ImVec2 CanvasAutomationAPI::CanvasToTile(ImVec2 canvas_pos) const {
|
||||
int tile_size = GetDimensions().tile_size;
|
||||
float scale = canvas_->global_scale();
|
||||
|
||||
float tile_x = canvas_pos.x / (tile_size * scale);
|
||||
float tile_y = canvas_pos.y / (tile_size * scale);
|
||||
|
||||
return ImVec2(std::floor(tile_x), std::floor(tile_y));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Callback Registration
|
||||
// ============================================================================
|
||||
|
||||
void CanvasAutomationAPI::SetTilePaintCallback(TilePaintCallback callback) {
|
||||
tile_paint_callback_ = std::move(callback);
|
||||
}
|
||||
|
||||
void CanvasAutomationAPI::SetTileQueryCallback(TileQueryCallback callback) {
|
||||
tile_query_callback_ = std::move(callback);
|
||||
}
|
||||
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
|
||||
224
src/app/gui/canvas/canvas_automation_api.h
Normal file
224
src/app/gui/canvas/canvas_automation_api.h
Normal file
@@ -0,0 +1,224 @@
|
||||
#ifndef YAZE_APP_GUI_CANVAS_CANVAS_AUTOMATION_API_H
|
||||
#define YAZE_APP_GUI_CANVAS_CANVAS_AUTOMATION_API_H
|
||||
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
// Forward declaration
|
||||
class Canvas;
|
||||
|
||||
/**
|
||||
* @brief Programmatic interface for controlling canvas operations.
|
||||
*
|
||||
* Enables z3ed CLI, AI agents, GUI automation, and remote control via gRPC.
|
||||
* All operations work with logical tile coordinates, independent of zoom/scroll.
|
||||
*/
|
||||
class CanvasAutomationAPI {
|
||||
public:
|
||||
/**
|
||||
* @brief Selection state returned by GetSelection().
|
||||
*/
|
||||
struct SelectionState {
|
||||
bool has_selection = false;
|
||||
std::vector<ImVec2> selected_tiles;
|
||||
ImVec2 selection_start = {0, 0};
|
||||
ImVec2 selection_end = {0, 0};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Canvas dimensions in logical tile units.
|
||||
*/
|
||||
struct Dimensions {
|
||||
int width_tiles = 0;
|
||||
int height_tiles = 0;
|
||||
int tile_size = 16;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Visible region in logical tile coordinates.
|
||||
*/
|
||||
struct VisibleRegion {
|
||||
int min_x = 0;
|
||||
int min_y = 0;
|
||||
int max_x = 0;
|
||||
int max_y = 0;
|
||||
};
|
||||
|
||||
explicit CanvasAutomationAPI(Canvas* canvas);
|
||||
|
||||
// ============================================================================
|
||||
// Tile Operations
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Paint a single tile at logical coordinates.
|
||||
* @param x Logical X coordinate (tile units)
|
||||
* @param y Logical Y coordinate (tile units)
|
||||
* @param tile_id Tile ID to paint
|
||||
* @return true if successful, false if out of bounds
|
||||
*/
|
||||
bool SetTileAt(int x, int y, int tile_id);
|
||||
|
||||
/**
|
||||
* @brief Query tile ID at logical coordinates.
|
||||
* @param x Logical X coordinate
|
||||
* @param y Logical Y coordinate
|
||||
* @return Tile ID at position, or -1 if out of bounds
|
||||
*/
|
||||
int GetTileAt(int x, int y) const;
|
||||
|
||||
/**
|
||||
* @brief Paint multiple tiles in a batch operation.
|
||||
* @param tiles Vector of (x, y, tile_id) tuples
|
||||
* @return true if all tiles painted successfully
|
||||
*/
|
||||
bool SetTiles(const std::vector<std::tuple<int, int, int>>& tiles);
|
||||
|
||||
// ============================================================================
|
||||
// Selection Operations
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Select a single tile.
|
||||
* @param x Logical X coordinate
|
||||
* @param y Logical Y coordinate
|
||||
*/
|
||||
void SelectTile(int x, int y);
|
||||
|
||||
/**
|
||||
* @brief Select a rectangular region of tiles.
|
||||
* @param x1 Top-left X coordinate (logical)
|
||||
* @param y1 Top-left Y coordinate (logical)
|
||||
* @param x2 Bottom-right X coordinate (logical)
|
||||
* @param y2 Bottom-right Y coordinate (logical)
|
||||
*/
|
||||
void SelectTileRect(int x1, int y1, int x2, int y2);
|
||||
|
||||
/**
|
||||
* @brief Query current selection state.
|
||||
* @return Selection state with all selected tiles
|
||||
*/
|
||||
SelectionState GetSelection() const;
|
||||
|
||||
/**
|
||||
* @brief Clear current selection.
|
||||
*/
|
||||
void ClearSelection();
|
||||
|
||||
// ============================================================================
|
||||
// View Operations
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Scroll canvas to make tile visible.
|
||||
* @param x Logical X coordinate
|
||||
* @param y Logical Y coordinate
|
||||
* @param center If true, center the tile; otherwise just ensure visibility
|
||||
*/
|
||||
void ScrollToTile(int x, int y, bool center = false);
|
||||
|
||||
/**
|
||||
* @brief Center canvas view on a specific tile.
|
||||
* @param x Logical X coordinate
|
||||
* @param y Logical Y coordinate
|
||||
*/
|
||||
void CenterOn(int x, int y);
|
||||
|
||||
/**
|
||||
* @brief Set canvas zoom level.
|
||||
* @param zoom Zoom factor (0.25 to 4.0, default 1.0)
|
||||
*/
|
||||
void SetZoom(float zoom);
|
||||
|
||||
/**
|
||||
* @brief Get current zoom level.
|
||||
* @return Current zoom factor
|
||||
*/
|
||||
float GetZoom() const;
|
||||
|
||||
// ============================================================================
|
||||
// Query Operations
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Get canvas dimensions in logical tile units.
|
||||
* @return Canvas dimensions
|
||||
*/
|
||||
Dimensions GetDimensions() const;
|
||||
|
||||
/**
|
||||
* @brief Get currently visible tile region.
|
||||
* @return Visible region bounds in logical coordinates
|
||||
*/
|
||||
VisibleRegion GetVisibleRegion() const;
|
||||
|
||||
/**
|
||||
* @brief Check if a tile is currently visible.
|
||||
* @param x Logical X coordinate
|
||||
* @param y Logical Y coordinate
|
||||
* @return true if tile is visible on canvas
|
||||
*/
|
||||
bool IsTileVisible(int x, int y) const;
|
||||
|
||||
/**
|
||||
* @brief Check if coordinates are within canvas bounds.
|
||||
* @param x Logical X coordinate
|
||||
* @param y Logical Y coordinate
|
||||
* @return true if coordinates are valid
|
||||
*/
|
||||
bool IsInBounds(int x, int y) const;
|
||||
|
||||
// ============================================================================
|
||||
// Coordinate Conversion
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Convert logical tile coordinates to canvas pixel coordinates.
|
||||
* @param x Logical X coordinate
|
||||
* @param y Logical Y coordinate
|
||||
* @return Canvas pixel position
|
||||
*/
|
||||
ImVec2 TileToCanvas(int x, int y) const;
|
||||
|
||||
/**
|
||||
* @brief Convert canvas pixel coordinates to logical tile coordinates.
|
||||
* @param canvas_pos Canvas pixel position
|
||||
* @return Logical tile position
|
||||
*/
|
||||
ImVec2 CanvasToTile(ImVec2 canvas_pos) const;
|
||||
|
||||
// ============================================================================
|
||||
// Callback Registration (for external integrations)
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Set callback for tile painting operations.
|
||||
* Allows external systems (CLI, AI agents) to implement custom tile logic.
|
||||
*/
|
||||
using TilePaintCallback = std::function<bool(int x, int y, int tile_id)>;
|
||||
void SetTilePaintCallback(TilePaintCallback callback);
|
||||
|
||||
/**
|
||||
* @brief Set callback for tile querying operations.
|
||||
* Allows external systems to provide tile data.
|
||||
*/
|
||||
using TileQueryCallback = std::function<int(int x, int y)>;
|
||||
void SetTileQueryCallback(TileQueryCallback callback);
|
||||
|
||||
private:
|
||||
Canvas* canvas_;
|
||||
TilePaintCallback tile_paint_callback_;
|
||||
TileQueryCallback tile_query_callback_;
|
||||
};
|
||||
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_GUI_CANVAS_CANVAS_AUTOMATION_API_H
|
||||
|
||||
Reference in New Issue
Block a user