This guide provides a comprehensive overview of the yaze canvas system, its architecture, and best practices for integration. It reflects the state of the system after the October 2025 refactoring.
The canvas system was refactored from a monolithic class into a modular, component-based architecture. The main gui::Canvas class now acts as a façade, coordinating a set of single-responsibility components and free functions.
CanvasState and CanvasConfig hold state, which is operated on by free functions.zelda3 specifics, making it reusable for any editor.The majority of the canvas code resides in src/app/gui/canvas/.
CanvasConfig)gui::CanvasConfig struct (defined in canvas_config.h) holds all configuration for a canvas instance.CanvasState)canvas_state.h) that holds the dynamic state of the canvas, including geometry, zoom, and scroll.The canvas operates with three distinct coordinate spaces. Using the correct one is critical to avoid bugs.
ImGui::GetIO().MousePos). Never use this for canvas logic.Canvas::hover_mouse_pos() to get this. This is the correct space for entity positioning and high-level calculations.Canvas::CanvasToTile() to convert from world space.A critical fix was made to ensure Canvas::hover_mouse_pos() is updated continuously whenever the canvas is hovered, decoupling it from specific actions like painting.
The canvas supports several interaction modes, managed via the CanvasUsage enum.
kTilePainting: For painting tiles onto a tilemap.kTileSelection: For selecting one or more tiles.kRectangleSelection: For drag-selecting a rectangular area.kEntityManipulation: For moving and interacting with entities.kPaletteEditing: For palette-related work.kDiagnostics: For performance and debug overlays.Set the mode using canvas.SetUsageMode(gui::CanvasUsage::kTilePainting). This ensures the context menu and interaction handlers behave correctly.
Interaction logic is moving towards an event-driven model. Instead of inspecting canvas state directly, editors should handle events returned by interaction functions.
Example:
The context menu system is now unified, data-driven, and supports persistent popups.
gui::CanvasMenuItem struct.MenuSectionPriority enum, ensuring a consistent layout:kEditorSpecific (highest priority)kBitmapOperationskCanvasPropertieskDebug (lowest priority)gui::CanvasMenuBuilder provides a clean, chainable API for constructing complex menus.Add a Simple Menu Item:
Add a Declarative Popup Item: This pattern automatically handles popup registration and persistence.
Build a Complex Menu with the Builder:
A generic, Zelda-agnostic entity system allows editors to manage on-canvas objects.
src/app/editor/overworld/operations/entity_operations.h, such as InsertEntrance, InsertSprite, etc. These functions are designed for ZScream feature parity.OverworldEditor delegates to the MapPropertiesSystem, which in turn calls these flat functions to modify the ROM state.kEntityManipulation mode.Usage Flow:
kEntityManipulation.Insert... function.Canvas: Instantiate gui::Canvas, providing a unique ID.CanvasUsage mode via canvas.SetUsageMode(). Configure available modes and other options in the CanvasConfig struct.finish_paint.canvas.Begin(size).canvas.End(). This handles rendering the grid, overlays, and the context menu.canvas.AddContextMenuItem() or the CanvasMenuBuilder to add editor-specific actions to the context menu. Assign the kEditorSpecific priority to ensure they appear at the top.CanvasState or handling events returned from interaction helpers.If you encounter issues with the canvas, check the following:
config.enable_context_menu true?CanvasMenuItem::WithPopup pattern?canvas.End() being called every frame to allow the PopupRegistry to render?canvas.hover_mouse_pos() for world coordinates instead of ImGui::GetIO().MousePos?MenuSectionPriority for your custom menu sections?The CanvasAutomationAPI provides hooks for testing and automation. It allows for programmatic control of tile operations (SetTileAt, SelectRect), view controls (ScrollToTile, SetZoom), and entity manipulation. This API is exposed via the z3ed CLI and a gRPC service.