tilemap-editor 3.2.3__tar.gz → 3.2.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tilemap_editor-3.2.3/src/tilemap_editor.egg-info → tilemap_editor-3.2.5}/PKG-INFO +1 -1
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/pyproject.toml +1 -1
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/editor.py +53 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/tileset_collision/editor.py +46 -1
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/tileset_collision/standalone.py +18 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5/src/tilemap_editor.egg-info}/PKG-INFO +1 -1
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor.egg-info/SOURCES.txt +8 -1
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/widgets/layer_selector.py +9 -9
- tilemap_editor-3.2.5/tests/test_animation_library_grid_offset.py +150 -0
- tilemap_editor-3.2.5/tests/test_editor_pan_mode.py +324 -0
- tilemap_editor-3.2.5/tests/test_sprite_animation_editor_grid.py +145 -0
- tilemap_editor-3.2.5/tests/test_tile_grid_selection.py +573 -0
- tilemap_editor-3.2.5/tests/test_tile_selector_pick.py +193 -0
- tilemap_editor-3.2.5/tests/test_timeline_invalidate_cache.py +154 -0
- tilemap_editor-3.2.5/tests/test_toolbar_tools.py +208 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/LICENSE +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/MANIFEST.in +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/README.md +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/icons/arrow-down.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/icons/check.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/icons/checked.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/icons/close.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/icons/duplicate.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/icons/error.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/icons/file.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/icons/filedead.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/icons/fit.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/icons/folder.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/icons/grid.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/icons/image.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/icons/info.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/icons/load.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/icons/loop.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/icons/pan.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/icons/pause.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/icons/pencil.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/icons/play.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/icons/plus.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/icons/radio.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/icons/reset.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/icons/save.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/icons/stop.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/icons/tilemap.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/icons/tileset.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/icons/unchecked.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/icons/warning.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/icons/zoomin.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/assets/icons/zoomout.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/setup.cfg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/configs/themes.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/constants.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/event_map.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/layers.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/main.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/__init__.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/character_collision/__init__.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/character_collision/editor.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/character_collision/models.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/character_collision/protocols.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/character_collision/shape_editor.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/character_collision/standalone.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/object_tileset_collision/__init__.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/object_tileset_collision/editor.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/object_tileset_collision/models.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/object_tileset_collision/protocols.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/object_tileset_collision/standalone.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/sprite_animation/__init__.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/sprite_animation/__main__.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/sprite_animation/clipboard_util.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/sprite_animation/editor.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/sprite_animation/frame_picker.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/sprite_animation/models.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/sprite_animation/preview.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/sprite_animation/protocols.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/sprite_animation/runtime_load.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/sprite_animation/standalone.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/sprite_animation/timeline.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/sprite_animation/validation.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/sprite_editor/__init__.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/sprite_editor/editor.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/sprite_editor/standalone.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/tileset_collision/__init__.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/tileset_collision/collision_painter.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/tileset_collision/models.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/plugins/tileset_collision/protocols.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/standalone_automap.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/standalone_error_console.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/standalone_filemanager.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/standalone_image_viewer.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/__init__.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/__main__.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/__init__.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/icons/ToolMove.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/icons/arrow-down.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/icons/check.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/icons/checked.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/icons/close.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/icons/duplicate.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/icons/error.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/icons/file.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/icons/filedead.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/icons/fit.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/icons/folder.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/icons/grid.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/icons/image.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/icons/info.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/icons/load.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/icons/loop.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/icons/pan.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/icons/pause.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/icons/pencil.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/icons/play.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/icons/plus.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/icons/radio.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/icons/reset.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/icons/save.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/icons/stop.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/icons/tilemap.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/icons/tileset.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/icons/unchecked.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/icons/warning.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/icons/zoomin.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/assets/icons/zoomout.svg +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/cli.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/main.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor/settings.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor.egg-info/dependency_links.txt +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor.egg-info/entry_points.txt +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor.egg-info/requires.txt +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/tilemap_editor.egg-info/top_level.txt +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/ttypes/__init__.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/ttypes/tilemap.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/utils/__init__.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/utils/editor_preference.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/utils/error_handler.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/utils/font_manager.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/utils/history.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/utils/icon_manager.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/utils/icons_cache.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/utils/log_capture.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/utils/project_paths.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/utils/serialization.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/utils/standalone.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/utils/validation.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/widgets/__init__.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/widgets/automap_models.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/widgets/autotile_template.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/widgets/autotiler.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/widgets/filemanager.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/widgets/input.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/widgets/map_properties.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/widgets/mapsetup.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/widgets/regex_automap_designer.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/widgets/spritesheet_grid.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/widgets/tile_grid.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/widgets/tile_selector.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/widgets/ui/collision_layer_mask.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/widgets/ui/collision_layer_sidebar.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/widgets/ui/draw_utils.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/widgets/ui/fileinput.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/widgets/ui/layer_type_dialog.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/widgets/ui/menubar.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/widgets/ui/mode_indicator.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/widgets/ui/notification.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/widgets/ui/property_editor.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/widgets/ui/region_selector.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/widgets/ui/status_bar.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/widgets/ui/theme.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/widgets/ui/tileset_type_dialog.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/widgets/ui/toolbar.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/src/widgets/ui/tooltip.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/tests/test_collision_layer_mask.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/tests/test_collision_layer_sidebar.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/tests/test_project_paths.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/tests/test_render_scale.py +0 -0
- {tilemap_editor-3.2.3 → tilemap_editor-3.2.5}/tests/test_tilemap_save.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tilemap-editor"
|
|
7
|
-
version = "3.2.
|
|
7
|
+
version = "3.2.5"
|
|
8
8
|
description = "Pygame tilemap editor with SVG icons, professional UI, and sprite animation tools"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -823,6 +823,11 @@ class Editor:
|
|
|
823
823
|
|
|
824
824
|
args = [str(path), "--tile-size", tile_size, "--data-root", str(self.data_root)]
|
|
825
825
|
|
|
826
|
+
# Collect auto-tile variant groups for this tileset and pass as --propagation-groups
|
|
827
|
+
propagation_groups_path = self._write_propagation_groups(path)
|
|
828
|
+
if propagation_groups_path:
|
|
829
|
+
args.extend(["--propagation-groups", str(propagation_groups_path)])
|
|
830
|
+
|
|
826
831
|
collision_dir = self.data_root / self.config.get("collision_paths", {}).get("tileset", "collision")
|
|
827
832
|
collision_path = collision_dir / f"{path.stem}.collision.json"
|
|
828
833
|
if collision_path.exists():
|
|
@@ -841,6 +846,54 @@ class Editor:
|
|
|
841
846
|
if logger:
|
|
842
847
|
logger.info(msg)
|
|
843
848
|
|
|
849
|
+
def _write_propagation_groups(self, tileset_path: Path) -> Optional[Path]:
|
|
850
|
+
"""Collect auto-tile variant groups for a tileset and write to temp JSON.
|
|
851
|
+
|
|
852
|
+
Returns the path to the temp file, or None if no autotiler data is available.
|
|
853
|
+
"""
|
|
854
|
+
if not hasattr(self, "autotiler") or not self.autotiler:
|
|
855
|
+
return None
|
|
856
|
+
|
|
857
|
+
tw = getattr(self, "tileset_widget", None)
|
|
858
|
+
if not tw or not tw.tilesets:
|
|
859
|
+
return None
|
|
860
|
+
|
|
861
|
+
# Find the tileset index matching the given path
|
|
862
|
+
resolved_path = Path(tileset_path).resolve()
|
|
863
|
+
tileset_index = None
|
|
864
|
+
for idx, ts in enumerate(tw.tilesets):
|
|
865
|
+
try:
|
|
866
|
+
if Path(ts.path).resolve() == resolved_path:
|
|
867
|
+
tileset_index = idx
|
|
868
|
+
break
|
|
869
|
+
except (OSError, ValueError):
|
|
870
|
+
continue
|
|
871
|
+
|
|
872
|
+
if tileset_index is None:
|
|
873
|
+
return None
|
|
874
|
+
|
|
875
|
+
# Collect variant_ids grouped by group_id from autotile rules matching this tileset
|
|
876
|
+
groups: Dict[str, List[int]] = {}
|
|
877
|
+
for group in self.autotiler.groups:
|
|
878
|
+
for rule in group.rules:
|
|
879
|
+
if rule.tileset_index == tileset_index and rule.variant_ids:
|
|
880
|
+
gid = rule.group_id or group.name
|
|
881
|
+
if gid not in groups:
|
|
882
|
+
groups[gid] = []
|
|
883
|
+
groups[gid].extend(rule.variant_ids)
|
|
884
|
+
|
|
885
|
+
if not groups:
|
|
886
|
+
return None
|
|
887
|
+
|
|
888
|
+
import json
|
|
889
|
+
import tempfile
|
|
890
|
+
fd, tmp_path = tempfile.mkstemp(suffix=".json", prefix="propagation_groups_")
|
|
891
|
+
with os.fdopen(fd, "w") as f:
|
|
892
|
+
json.dump(groups, f)
|
|
893
|
+
|
|
894
|
+
print(f"[COLLISION] Wrote {len(groups)} propagation groups to {tmp_path}")
|
|
895
|
+
return Path(tmp_path)
|
|
896
|
+
|
|
844
897
|
def _launch_object_tileset_collision_editor_with_image(self, path: Path):
|
|
845
898
|
"""Launch object tileset collision editor (region-based)."""
|
|
846
899
|
logger = self.logger if hasattr(self, 'logger') else None
|
|
@@ -72,6 +72,9 @@ class TilesetCollisionEditor:
|
|
|
72
72
|
self._toast_timer: float = 0.0
|
|
73
73
|
self._toast_start: int = 0
|
|
74
74
|
|
|
75
|
+
# Auto-tile propagation: group_id -> [variant_ids]
|
|
76
|
+
self._propagation_groups: Dict[str, List[int]] = {}
|
|
77
|
+
|
|
75
78
|
# Collision library
|
|
76
79
|
self.library = TilesetCollisionLibrary(
|
|
77
80
|
tileset_name=self._tileset_name,
|
|
@@ -436,13 +439,53 @@ class TilesetCollisionEditor:
|
|
|
436
439
|
error_handler.capture(e, context="load_collision_data")
|
|
437
440
|
|
|
438
441
|
def save_to_file(self, path: Path) -> None:
|
|
439
|
-
"""Save collision data to file"""
|
|
442
|
+
"""Save collision data to file, auto-propagating within auto-tile groups."""
|
|
440
443
|
try:
|
|
441
444
|
self._save_tile_collision_for_selection()
|
|
445
|
+
self._propagate_collision_to_groups()
|
|
442
446
|
self.library.save(path)
|
|
443
447
|
except Exception as e:
|
|
444
448
|
error_handler.capture(e, context="save_collision_file")
|
|
445
449
|
|
|
450
|
+
def _propagate_collision_to_groups(self) -> None:
|
|
451
|
+
"""Propagate collision shapes within each auto-tile group.
|
|
452
|
+
|
|
453
|
+
If any tile in a propagation group has collision shapes, those shapes
|
|
454
|
+
are copied to all other tiles in the same group that don't already
|
|
455
|
+
have their own collision data.
|
|
456
|
+
"""
|
|
457
|
+
if not self._propagation_groups:
|
|
458
|
+
return
|
|
459
|
+
|
|
460
|
+
propagated_count = 0
|
|
461
|
+
for group_id, variant_ids in self._propagation_groups.items():
|
|
462
|
+
# Find the first tile in this group that has collision shapes
|
|
463
|
+
source_shapes = None
|
|
464
|
+
for vid in variant_ids:
|
|
465
|
+
tile_data = self.library.tiles.get(vid)
|
|
466
|
+
if tile_data and tile_data.shapes:
|
|
467
|
+
source_shapes = tile_data.shapes
|
|
468
|
+
break
|
|
469
|
+
|
|
470
|
+
if source_shapes is None:
|
|
471
|
+
continue
|
|
472
|
+
|
|
473
|
+
# Propagate to all variants in the group that lack collision
|
|
474
|
+
for vid in variant_ids:
|
|
475
|
+
existing = self.library.tiles.get(vid)
|
|
476
|
+
if existing is None:
|
|
477
|
+
self.library.tiles[vid] = TileCollisionData(
|
|
478
|
+
tile_id=vid,
|
|
479
|
+
shapes=[CollisionPolygon(
|
|
480
|
+
vertices=list(s.vertices),
|
|
481
|
+
one_way=s.one_way,
|
|
482
|
+
) for s in source_shapes],
|
|
483
|
+
)
|
|
484
|
+
propagated_count += 1
|
|
485
|
+
|
|
486
|
+
if propagated_count:
|
|
487
|
+
self._show_toast(f"Propagated collision to {propagated_count} auto-tile variants")
|
|
488
|
+
|
|
446
489
|
def load_from_file(self, path: Path) -> None:
|
|
447
490
|
"""Load collision data from file"""
|
|
448
491
|
try:
|
|
@@ -829,6 +872,7 @@ class TilesetCollisionEditor:
|
|
|
829
872
|
tile_size: Tuple[int, int] = (32, 32),
|
|
830
873
|
window_size: Tuple[int, int] = (1200, 800),
|
|
831
874
|
data_root: Path = None,
|
|
875
|
+
propagation_groups: Optional[Dict[str, List[int]]] = None,
|
|
832
876
|
) -> "TilesetCollisionEditor":
|
|
833
877
|
"""Create editor from tileset image path (for standalone use)"""
|
|
834
878
|
surface = pygame.image.load(tileset_path).convert_alpha()
|
|
@@ -838,6 +882,7 @@ class TilesetCollisionEditor:
|
|
|
838
882
|
editor._tileset_path_stem = tileset_path.stem
|
|
839
883
|
editor._tileset_name = tileset_path.stem # Use actual filename, not "Tileset"
|
|
840
884
|
editor.library.tileset_name = tileset_path.stem
|
|
885
|
+
editor._propagation_groups = propagation_groups or {}
|
|
841
886
|
return editor
|
|
842
887
|
|
|
843
888
|
def run(self) -> None:
|
|
@@ -86,6 +86,12 @@ def main(argv: list[str] | None = None) -> None:
|
|
|
86
86
|
required=True,
|
|
87
87
|
help="Path to project data root directory (REQUIRED)",
|
|
88
88
|
)
|
|
89
|
+
parser.add_argument(
|
|
90
|
+
"--propagation-groups",
|
|
91
|
+
type=Path,
|
|
92
|
+
default=None,
|
|
93
|
+
help="Path to JSON file mapping auto-tile group IDs to variant ID lists",
|
|
94
|
+
)
|
|
89
95
|
|
|
90
96
|
args = parser.parse_args(argv)
|
|
91
97
|
|
|
@@ -107,11 +113,23 @@ def main(argv: list[str] | None = None) -> None:
|
|
|
107
113
|
screen = pygame.display.set_mode(window_size, pygame.RESIZABLE)
|
|
108
114
|
pygame.display.set_caption(f"Tileset Collision Editor — {args.image.name}")
|
|
109
115
|
|
|
116
|
+
propagation_groups = None
|
|
117
|
+
if args.propagation_groups and args.propagation_groups.exists():
|
|
118
|
+
import json
|
|
119
|
+
with open(args.propagation_groups) as f:
|
|
120
|
+
propagation_groups = json.load(f)
|
|
121
|
+
print(f"Loaded {len(propagation_groups)} auto-tile propagation groups")
|
|
122
|
+
try:
|
|
123
|
+
args.propagation_groups.unlink()
|
|
124
|
+
except OSError:
|
|
125
|
+
pass
|
|
126
|
+
|
|
110
127
|
editor = TilesetCollisionEditor.from_path(
|
|
111
128
|
args.image,
|
|
112
129
|
tile_size=tile_size,
|
|
113
130
|
window_size=window_size,
|
|
114
131
|
data_root=data_root,
|
|
132
|
+
propagation_groups=propagation_groups,
|
|
115
133
|
)
|
|
116
134
|
|
|
117
135
|
if args.load and args.load.exists():
|
|
@@ -177,8 +177,15 @@ src/widgets/ui/theme.py
|
|
|
177
177
|
src/widgets/ui/tileset_type_dialog.py
|
|
178
178
|
src/widgets/ui/toolbar.py
|
|
179
179
|
src/widgets/ui/tooltip.py
|
|
180
|
+
tests/test_animation_library_grid_offset.py
|
|
180
181
|
tests/test_collision_layer_mask.py
|
|
181
182
|
tests/test_collision_layer_sidebar.py
|
|
183
|
+
tests/test_editor_pan_mode.py
|
|
182
184
|
tests/test_project_paths.py
|
|
183
185
|
tests/test_render_scale.py
|
|
184
|
-
tests/
|
|
186
|
+
tests/test_sprite_animation_editor_grid.py
|
|
187
|
+
tests/test_tile_grid_selection.py
|
|
188
|
+
tests/test_tile_selector_pick.py
|
|
189
|
+
tests/test_tilemap_save.py
|
|
190
|
+
tests/test_timeline_invalidate_cache.py
|
|
191
|
+
tests/test_toolbar_tools.py
|
|
@@ -382,13 +382,6 @@ class LayerSelector:
|
|
|
382
382
|
pygame.draw.rect(screen, color, item_rect)
|
|
383
383
|
pygame.draw.rect(screen, COLORS.border_soft, item_rect, 1)
|
|
384
384
|
|
|
385
|
-
if i == self.renaming_layer_idx:
|
|
386
|
-
pygame.draw.rect(
|
|
387
|
-
screen,
|
|
388
|
-
COLORS.selected,
|
|
389
|
-
Rect(item_rect.x + 4, item_rect.y + 4, 120, 20),
|
|
390
|
-
border_radius=SHAPE.radius_sm,
|
|
391
|
-
)
|
|
392
385
|
mx, my = pygame.mouse.get_pos()
|
|
393
386
|
eye_rect = self._get_eye_icon_rect(i, (mx, my))
|
|
394
387
|
lock_rect = self._get_lock_icon_rect(i, (mx, my))
|
|
@@ -396,13 +389,20 @@ class LayerSelector:
|
|
|
396
389
|
self.editor.tooltip.show("Toggle Visibility", (mx + 10, my + 10))
|
|
397
390
|
elif lock_rect and lock_rect.collidepoint(mx, my):
|
|
398
391
|
self.editor.tooltip.show("Toggle Lock", (mx + 10, my + 10))
|
|
392
|
+
|
|
393
|
+
if i == self.renaming_layer_idx:
|
|
394
|
+
pygame.draw.rect(
|
|
395
|
+
screen,
|
|
396
|
+
COLORS.selected,
|
|
397
|
+
Rect(item_rect.x + 4, item_rect.y + 4, 120, 20),
|
|
398
|
+
border_radius=SHAPE.radius_sm,
|
|
399
|
+
)
|
|
399
400
|
name_txt = self.font_layer.render(
|
|
400
401
|
self.rename_text + "|", True, self.text_color
|
|
401
402
|
)
|
|
402
|
-
screen.blit(name_txt, (item_rect.x + 5, item_rect.y + 5))
|
|
403
403
|
else:
|
|
404
404
|
name_txt = self.font_layer.render(layer.name, True, self.text_color)
|
|
405
|
-
|
|
405
|
+
screen.blit(name_txt, (item_rect.x + 5, item_rect.y + 5))
|
|
406
406
|
|
|
407
407
|
eye_x = item_rect.right - 25
|
|
408
408
|
eye_y = item_rect.y + 7
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for AnimationLibrary.grid_offset field added in PR.
|
|
3
|
+
|
|
4
|
+
Covers:
|
|
5
|
+
- Default value
|
|
6
|
+
- to_dict serializes grid_offset
|
|
7
|
+
- from_dict deserializes grid_offset
|
|
8
|
+
- from_dict falls back to (0, 0) when key is missing (backward compat)
|
|
9
|
+
- save/load round-trip preserves grid_offset
|
|
10
|
+
- grid_offset is a tuple (not list) after deserialization
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
import tempfile
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
import pytest
|
|
18
|
+
|
|
19
|
+
import sys
|
|
20
|
+
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class TestAnimationLibraryGridOffset:
|
|
24
|
+
"""Tests for the grid_offset field on AnimationLibrary."""
|
|
25
|
+
|
|
26
|
+
def test_default_grid_offset_is_zero(self):
|
|
27
|
+
from plugins.sprite_animation.models import AnimationLibrary
|
|
28
|
+
|
|
29
|
+
lib = AnimationLibrary()
|
|
30
|
+
assert lib.grid_offset == (0, 0)
|
|
31
|
+
|
|
32
|
+
def test_to_dict_includes_grid_offset(self):
|
|
33
|
+
from plugins.sprite_animation.models import AnimationLibrary
|
|
34
|
+
|
|
35
|
+
lib = AnimationLibrary(grid_offset=(4, 8))
|
|
36
|
+
d = lib.to_dict()
|
|
37
|
+
assert "grid_offset" in d
|
|
38
|
+
assert d["grid_offset"] == [4, 8]
|
|
39
|
+
|
|
40
|
+
def test_to_dict_zero_grid_offset(self):
|
|
41
|
+
from plugins.sprite_animation.models import AnimationLibrary
|
|
42
|
+
|
|
43
|
+
lib = AnimationLibrary(grid_offset=(0, 0))
|
|
44
|
+
d = lib.to_dict()
|
|
45
|
+
assert d["grid_offset"] == [0, 0]
|
|
46
|
+
|
|
47
|
+
def test_from_dict_reads_grid_offset(self):
|
|
48
|
+
from plugins.sprite_animation.models import AnimationLibrary
|
|
49
|
+
|
|
50
|
+
data = {
|
|
51
|
+
"spritesheet_path": None,
|
|
52
|
+
"tile_size": [32, 32],
|
|
53
|
+
"grid_offset": [10, 20],
|
|
54
|
+
"animations": {},
|
|
55
|
+
}
|
|
56
|
+
lib = AnimationLibrary.from_dict(data)
|
|
57
|
+
assert lib.grid_offset == (10, 20)
|
|
58
|
+
|
|
59
|
+
def test_from_dict_missing_grid_offset_defaults_to_zero(self):
|
|
60
|
+
"""Backward-compat: old JSON files without grid_offset key should load fine."""
|
|
61
|
+
from plugins.sprite_animation.models import AnimationLibrary
|
|
62
|
+
|
|
63
|
+
data = {
|
|
64
|
+
"spritesheet_path": None,
|
|
65
|
+
"tile_size": [32, 32],
|
|
66
|
+
"animations": {},
|
|
67
|
+
}
|
|
68
|
+
lib = AnimationLibrary.from_dict(data)
|
|
69
|
+
assert lib.grid_offset == (0, 0)
|
|
70
|
+
|
|
71
|
+
def test_from_dict_grid_offset_is_tuple(self):
|
|
72
|
+
"""grid_offset should be a tuple after deserialization, not a list."""
|
|
73
|
+
from plugins.sprite_animation.models import AnimationLibrary
|
|
74
|
+
|
|
75
|
+
data = {
|
|
76
|
+
"spritesheet_path": None,
|
|
77
|
+
"tile_size": [32, 32],
|
|
78
|
+
"grid_offset": [5, 3],
|
|
79
|
+
"animations": {},
|
|
80
|
+
}
|
|
81
|
+
lib = AnimationLibrary.from_dict(data)
|
|
82
|
+
assert isinstance(lib.grid_offset, tuple)
|
|
83
|
+
|
|
84
|
+
def test_save_load_round_trip_preserves_grid_offset(self):
|
|
85
|
+
"""save() then load() must preserve a non-zero grid_offset."""
|
|
86
|
+
from plugins.sprite_animation.models import AnimationLibrary
|
|
87
|
+
|
|
88
|
+
lib = AnimationLibrary(grid_offset=(7, 13), tile_size=(16, 16))
|
|
89
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
90
|
+
path = Path(tmp) / "anim.json"
|
|
91
|
+
lib.save(path)
|
|
92
|
+
loaded = AnimationLibrary.load(path)
|
|
93
|
+
|
|
94
|
+
assert loaded.grid_offset == (7, 13)
|
|
95
|
+
|
|
96
|
+
def test_save_load_round_trip_zero_grid_offset(self):
|
|
97
|
+
"""save() then load() preserves (0, 0) grid_offset."""
|
|
98
|
+
from plugins.sprite_animation.models import AnimationLibrary
|
|
99
|
+
|
|
100
|
+
lib = AnimationLibrary(grid_offset=(0, 0))
|
|
101
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
102
|
+
path = Path(tmp) / "anim.json"
|
|
103
|
+
lib.save(path)
|
|
104
|
+
loaded = AnimationLibrary.load(path)
|
|
105
|
+
|
|
106
|
+
assert loaded.grid_offset == (0, 0)
|
|
107
|
+
|
|
108
|
+
def test_to_dict_grid_offset_is_list(self):
|
|
109
|
+
"""grid_offset is serialized as a JSON list (not tuple) for JSON compat."""
|
|
110
|
+
from plugins.sprite_animation.models import AnimationLibrary
|
|
111
|
+
|
|
112
|
+
lib = AnimationLibrary(grid_offset=(2, 6))
|
|
113
|
+
d = lib.to_dict()
|
|
114
|
+
assert isinstance(d["grid_offset"], list)
|
|
115
|
+
|
|
116
|
+
def test_grid_offset_stored_alongside_tile_size(self):
|
|
117
|
+
"""Both tile_size and grid_offset appear in the serialized dict."""
|
|
118
|
+
from plugins.sprite_animation.models import AnimationLibrary
|
|
119
|
+
|
|
120
|
+
lib = AnimationLibrary(tile_size=(16, 16), grid_offset=(4, 4))
|
|
121
|
+
d = lib.to_dict()
|
|
122
|
+
assert d["tile_size"] == [16, 16]
|
|
123
|
+
assert d["grid_offset"] == [4, 4]
|
|
124
|
+
|
|
125
|
+
def test_from_dict_large_grid_offset(self):
|
|
126
|
+
"""Arbitrary large values are preserved."""
|
|
127
|
+
from plugins.sprite_animation.models import AnimationLibrary
|
|
128
|
+
|
|
129
|
+
data = {
|
|
130
|
+
"spritesheet_path": None,
|
|
131
|
+
"tile_size": [32, 32],
|
|
132
|
+
"grid_offset": [128, 256],
|
|
133
|
+
"animations": {},
|
|
134
|
+
}
|
|
135
|
+
lib = AnimationLibrary.from_dict(data)
|
|
136
|
+
assert lib.grid_offset == (128, 256)
|
|
137
|
+
|
|
138
|
+
def test_json_file_contains_grid_offset_key(self):
|
|
139
|
+
"""The written JSON file contains the grid_offset key."""
|
|
140
|
+
from plugins.sprite_animation.models import AnimationLibrary
|
|
141
|
+
|
|
142
|
+
lib = AnimationLibrary(grid_offset=(3, 9))
|
|
143
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
144
|
+
path = Path(tmp) / "anim.json"
|
|
145
|
+
lib.save(path)
|
|
146
|
+
with open(path) as f:
|
|
147
|
+
raw = json.load(f)
|
|
148
|
+
|
|
149
|
+
assert "grid_offset" in raw
|
|
150
|
+
assert raw["grid_offset"] == [3, 9]
|