tilemap-editor 3.2.2__tar.gz → 3.2.4__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.2/src/tilemap_editor.egg-info → tilemap_editor-3.2.4}/PKG-INFO +1 -1
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/pyproject.toml +1 -1
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/editor.py +121 -3
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/sprite_animation/editor.py +46 -2
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/sprite_animation/models.py +3 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/sprite_animation/timeline.py +4 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/tileset_collision/editor.py +46 -1
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/tileset_collision/standalone.py +18 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4/src/tilemap_editor.egg-info}/PKG-INFO +1 -1
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor.egg-info/SOURCES.txt +8 -1
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/widgets/tile_grid.py +699 -65
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/widgets/tile_selector.py +38 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/widgets/ui/toolbar.py +23 -0
- tilemap_editor-3.2.4/tests/test_animation_library_grid_offset.py +150 -0
- tilemap_editor-3.2.4/tests/test_editor_pan_mode.py +324 -0
- tilemap_editor-3.2.4/tests/test_sprite_animation_editor_grid.py +145 -0
- tilemap_editor-3.2.4/tests/test_tile_grid_selection.py +573 -0
- tilemap_editor-3.2.4/tests/test_tile_selector_pick.py +193 -0
- tilemap_editor-3.2.4/tests/test_timeline_invalidate_cache.py +154 -0
- tilemap_editor-3.2.4/tests/test_toolbar_tools.py +208 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/LICENSE +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/MANIFEST.in +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/README.md +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/icons/arrow-down.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/icons/check.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/icons/checked.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/icons/close.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/icons/duplicate.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/icons/error.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/icons/file.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/icons/filedead.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/icons/fit.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/icons/folder.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/icons/grid.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/icons/image.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/icons/info.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/icons/load.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/icons/loop.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/icons/pan.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/icons/pause.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/icons/pencil.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/icons/play.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/icons/plus.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/icons/radio.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/icons/reset.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/icons/save.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/icons/stop.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/icons/tilemap.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/icons/tileset.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/icons/unchecked.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/icons/warning.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/icons/zoomin.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/assets/icons/zoomout.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/setup.cfg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/configs/themes.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/constants.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/event_map.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/layers.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/main.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/__init__.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/character_collision/__init__.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/character_collision/editor.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/character_collision/models.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/character_collision/protocols.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/character_collision/shape_editor.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/character_collision/standalone.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/object_tileset_collision/__init__.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/object_tileset_collision/editor.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/object_tileset_collision/models.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/object_tileset_collision/protocols.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/object_tileset_collision/standalone.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/sprite_animation/__init__.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/sprite_animation/__main__.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/sprite_animation/clipboard_util.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/sprite_animation/frame_picker.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/sprite_animation/preview.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/sprite_animation/protocols.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/sprite_animation/runtime_load.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/sprite_animation/standalone.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/sprite_animation/validation.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/sprite_editor/__init__.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/sprite_editor/editor.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/sprite_editor/standalone.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/tileset_collision/__init__.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/tileset_collision/collision_painter.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/tileset_collision/models.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/plugins/tileset_collision/protocols.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/standalone_automap.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/standalone_error_console.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/standalone_filemanager.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/standalone_image_viewer.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/__init__.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/__main__.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/__init__.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/icons/ToolMove.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/icons/arrow-down.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/icons/check.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/icons/checked.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/icons/close.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/icons/duplicate.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/icons/error.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/icons/file.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/icons/filedead.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/icons/fit.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/icons/folder.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/icons/grid.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/icons/image.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/icons/info.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/icons/load.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/icons/loop.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/icons/pan.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/icons/pause.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/icons/pencil.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/icons/play.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/icons/plus.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/icons/radio.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/icons/reset.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/icons/save.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/icons/stop.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/icons/tilemap.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/icons/tileset.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/icons/unchecked.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/icons/warning.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/icons/zoomin.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/assets/icons/zoomout.svg +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/cli.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/main.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor/settings.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor.egg-info/dependency_links.txt +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor.egg-info/entry_points.txt +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor.egg-info/requires.txt +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/tilemap_editor.egg-info/top_level.txt +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/ttypes/__init__.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/ttypes/tilemap.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/utils/__init__.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/utils/editor_preference.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/utils/error_handler.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/utils/font_manager.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/utils/history.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/utils/icon_manager.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/utils/icons_cache.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/utils/log_capture.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/utils/project_paths.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/utils/serialization.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/utils/standalone.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/utils/validation.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/widgets/__init__.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/widgets/automap_models.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/widgets/autotile_template.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/widgets/autotiler.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/widgets/filemanager.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/widgets/input.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/widgets/layer_selector.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/widgets/map_properties.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/widgets/mapsetup.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/widgets/regex_automap_designer.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/widgets/spritesheet_grid.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/widgets/ui/collision_layer_mask.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/widgets/ui/collision_layer_sidebar.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/widgets/ui/draw_utils.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/widgets/ui/fileinput.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/widgets/ui/layer_type_dialog.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/widgets/ui/menubar.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/widgets/ui/mode_indicator.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/widgets/ui/notification.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/widgets/ui/property_editor.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/widgets/ui/region_selector.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/widgets/ui/status_bar.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/widgets/ui/theme.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/widgets/ui/tileset_type_dialog.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/src/widgets/ui/tooltip.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/tests/test_collision_layer_mask.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/tests/test_collision_layer_sidebar.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/tests/test_project_paths.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/tests/test_render_scale.py +0 -0
- {tilemap_editor-3.2.2 → tilemap_editor-3.2.4}/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.4"
|
|
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"
|
|
@@ -150,6 +150,9 @@ class Editor:
|
|
|
150
150
|
self.running = False
|
|
151
151
|
self.pan_mode = False
|
|
152
152
|
self.autotile_mode = False
|
|
153
|
+
self.eraser_mode = False
|
|
154
|
+
self.select_mode = False
|
|
155
|
+
self._prev_tool = None
|
|
153
156
|
|
|
154
157
|
if isinstance(size, tuple) and len(size) == 2:
|
|
155
158
|
self.screen = pygame.display.set_mode(size, pygame.RESIZABLE)
|
|
@@ -684,13 +687,17 @@ class Editor:
|
|
|
684
687
|
self.open_file_manager(
|
|
685
688
|
on_select=self._launch_animation_editor_with_image,
|
|
686
689
|
initial_dir=self.data_root,
|
|
687
|
-
allowed_exts=[".png", ".jpg", ".jpeg"],
|
|
690
|
+
allowed_exts=[".png", ".jpg", ".jpeg", ".json"],
|
|
688
691
|
mode="open",
|
|
689
692
|
)
|
|
690
693
|
|
|
691
694
|
def _launch_animation_editor_with_image(self, path: Path):
|
|
692
|
-
"""Launch animation editor subprocess with selected image."""
|
|
695
|
+
"""Launch animation editor subprocess with selected image or JSON."""
|
|
693
696
|
try:
|
|
697
|
+
if path.suffix.lower() == ".json":
|
|
698
|
+
self._launch_animation_editor_with_json(path)
|
|
699
|
+
return
|
|
700
|
+
|
|
694
701
|
tile_size = "32x32"
|
|
695
702
|
if hasattr(self.tilemap, "tile_size") and self.tilemap.tile_size:
|
|
696
703
|
tw, th = self.tilemap.tile_size
|
|
@@ -711,6 +718,47 @@ class Editor:
|
|
|
711
718
|
except Exception as e:
|
|
712
719
|
error_handler.capture(e, context="launch_animation_editor")
|
|
713
720
|
|
|
721
|
+
def _launch_animation_editor_with_json(self, path: Path) -> None:
|
|
722
|
+
"""Launch animation editor from a saved .anim.json file."""
|
|
723
|
+
try:
|
|
724
|
+
from plugins.sprite_animation.models import AnimationLibrary
|
|
725
|
+
from utils.project_paths import resolve_project_path
|
|
726
|
+
|
|
727
|
+
lib = AnimationLibrary.load(path)
|
|
728
|
+
spritesheet_path = lib.spritesheet_path
|
|
729
|
+
tile_size = lib.tile_size
|
|
730
|
+
|
|
731
|
+
if spritesheet_path:
|
|
732
|
+
resolved = resolve_project_path(
|
|
733
|
+
spritesheet_path,
|
|
734
|
+
path.parent,
|
|
735
|
+
fallback_roots=[self.base_path] if self.base_path else None,
|
|
736
|
+
must_exist=True,
|
|
737
|
+
)
|
|
738
|
+
else:
|
|
739
|
+
resolved = None
|
|
740
|
+
|
|
741
|
+
if not resolved or not resolved.exists():
|
|
742
|
+
print(f"Could not locate spritesheet for animation file: {path.name}")
|
|
743
|
+
return
|
|
744
|
+
|
|
745
|
+
args = [
|
|
746
|
+
str(resolved),
|
|
747
|
+
"--tile-size", f"{tile_size[0]}x{tile_size[1]}",
|
|
748
|
+
"--load", str(path),
|
|
749
|
+
"--data-root", str(self.data_root),
|
|
750
|
+
]
|
|
751
|
+
process = launch_standalone(
|
|
752
|
+
"plugins.sprite_animation.standalone",
|
|
753
|
+
args,
|
|
754
|
+
cwd=self.base_path,
|
|
755
|
+
text=True,
|
|
756
|
+
)
|
|
757
|
+
self.child_processes.append(process)
|
|
758
|
+
print(f"Launched animation editor with: {path.name} (spritesheet: {resolved.name})")
|
|
759
|
+
except Exception as e:
|
|
760
|
+
error_handler.capture(e, context="launch_animation_editor")
|
|
761
|
+
|
|
714
762
|
def launch_sprite_editor(self):
|
|
715
763
|
"""Launch the sprite editor in a new window."""
|
|
716
764
|
self.open_file_manager(
|
|
@@ -775,6 +823,11 @@ class Editor:
|
|
|
775
823
|
|
|
776
824
|
args = [str(path), "--tile-size", tile_size, "--data-root", str(self.data_root)]
|
|
777
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
|
+
|
|
778
831
|
collision_dir = self.data_root / self.config.get("collision_paths", {}).get("tileset", "collision")
|
|
779
832
|
collision_path = collision_dir / f"{path.stem}.collision.json"
|
|
780
833
|
if collision_path.exists():
|
|
@@ -793,6 +846,54 @@ class Editor:
|
|
|
793
846
|
if logger:
|
|
794
847
|
logger.info(msg)
|
|
795
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
|
+
|
|
796
897
|
def _launch_object_tileset_collision_editor_with_image(self, path: Path):
|
|
797
898
|
"""Launch object tileset collision editor (region-based)."""
|
|
798
899
|
logger = self.logger if hasattr(self, 'logger') else None
|
|
@@ -1018,7 +1119,24 @@ class Editor:
|
|
|
1018
1119
|
self.perform_load()
|
|
1019
1120
|
continue
|
|
1020
1121
|
elif event.key == pygame.K_SPACE:
|
|
1021
|
-
|
|
1122
|
+
if self.pan_mode:
|
|
1123
|
+
# Restoring: turn off pan, re-enable previous tool
|
|
1124
|
+
self.pan_mode = False
|
|
1125
|
+
if getattr(self, "_prev_tool", None) == "select":
|
|
1126
|
+
self.select_mode = True
|
|
1127
|
+
elif getattr(self, "_prev_tool", None) == "eraser":
|
|
1128
|
+
self.eraser_mode = True
|
|
1129
|
+
else:
|
|
1130
|
+
# Entering pan: save current tool, turn off others
|
|
1131
|
+
if self.select_mode:
|
|
1132
|
+
self._prev_tool = "select"
|
|
1133
|
+
elif self.eraser_mode:
|
|
1134
|
+
self._prev_tool = "eraser"
|
|
1135
|
+
else:
|
|
1136
|
+
self._prev_tool = None
|
|
1137
|
+
self.pan_mode = True
|
|
1138
|
+
self.select_mode = False
|
|
1139
|
+
self.eraser_mode = False
|
|
1022
1140
|
continue
|
|
1023
1141
|
elif event.mod & pygame.KMOD_CTRL and event.key == pygame.K_g:
|
|
1024
1142
|
self.toggle_grid()
|
|
@@ -1367,7 +1367,7 @@ class SpriteAnimationEditor:
|
|
|
1367
1367
|
self.preview.set_surface(self._surface, self._tile_size)
|
|
1368
1368
|
self.timeline.surface = self._surface
|
|
1369
1369
|
self.timeline.tile_size = self._tile_size
|
|
1370
|
-
self.timeline.
|
|
1370
|
+
self.timeline.invalidate_cache()
|
|
1371
1371
|
|
|
1372
1372
|
# Update input fields to show clamped values
|
|
1373
1373
|
self._frame_width_input = str(width)
|
|
@@ -1404,7 +1404,7 @@ class SpriteAnimationEditor:
|
|
|
1404
1404
|
if hasattr(self.timeline, "grid_offset_x"):
|
|
1405
1405
|
self.timeline.grid_offset_x = offset_x
|
|
1406
1406
|
self.timeline.grid_offset_y = offset_y
|
|
1407
|
-
self.timeline.
|
|
1407
|
+
self.timeline.invalidate_cache()
|
|
1408
1408
|
|
|
1409
1409
|
# Update preview with offset (if it has the attribute)
|
|
1410
1410
|
if hasattr(self.preview, "grid_offset_x"):
|
|
@@ -1420,6 +1420,43 @@ class SpriteAnimationEditor:
|
|
|
1420
1420
|
self._offset_x_input = str(self._grid_offset_x)
|
|
1421
1421
|
self._offset_y_input = str(self._grid_offset_y)
|
|
1422
1422
|
|
|
1423
|
+
def _apply_library_grid_settings(self) -> None:
|
|
1424
|
+
"""Sync tile_size and grid_offset from the loaded library to all widgets."""
|
|
1425
|
+
tw, th = self.library.tile_size
|
|
1426
|
+
self._tile_size = (tw, th)
|
|
1427
|
+
self.library.tile_size = (tw, th)
|
|
1428
|
+
|
|
1429
|
+
# Update frame picker
|
|
1430
|
+
self.frame_picker.set_surface(self._surface, self._tile_size)
|
|
1431
|
+
|
|
1432
|
+
# Update preview and timeline
|
|
1433
|
+
self.preview.set_surface(self._surface, self._tile_size)
|
|
1434
|
+
self.timeline.surface = self._surface
|
|
1435
|
+
self.timeline.tile_size = self._tile_size
|
|
1436
|
+
self.timeline.invalidate_cache()
|
|
1437
|
+
|
|
1438
|
+
# Update frame size input fields
|
|
1439
|
+
self._frame_width_input = str(tw)
|
|
1440
|
+
self._frame_height_input = str(th)
|
|
1441
|
+
|
|
1442
|
+
# Sync grid offset from library
|
|
1443
|
+
gx, gy = self.library.grid_offset
|
|
1444
|
+
self._grid_offset_x = gx
|
|
1445
|
+
self._grid_offset_y = gy
|
|
1446
|
+
self._offset_x_input = str(gx)
|
|
1447
|
+
self._offset_y_input = str(gy)
|
|
1448
|
+
|
|
1449
|
+
# Propagate offset to all widgets
|
|
1450
|
+
if hasattr(self.frame_picker, "set_grid_offset"):
|
|
1451
|
+
self.frame_picker.set_grid_offset(gx, gy)
|
|
1452
|
+
if hasattr(self.timeline, "grid_offset_x"):
|
|
1453
|
+
self.timeline.grid_offset_x = gx
|
|
1454
|
+
self.timeline.grid_offset_y = gy
|
|
1455
|
+
self.timeline.invalidate_cache()
|
|
1456
|
+
if hasattr(self.preview, "grid_offset_x"):
|
|
1457
|
+
self.preview.grid_offset_x = gx
|
|
1458
|
+
self.preview.grid_offset_y = gy
|
|
1459
|
+
|
|
1423
1460
|
def _sync_active_animation(self) -> None:
|
|
1424
1461
|
"""Push current animation data to sub-widgets."""
|
|
1425
1462
|
anim = self._get_active()
|
|
@@ -1843,6 +1880,7 @@ class SpriteAnimationEditor:
|
|
|
1843
1880
|
print(f"Warning: Could not import FileManager: {e}")
|
|
1844
1881
|
path = self._default_save_path()
|
|
1845
1882
|
try:
|
|
1883
|
+
self.library.grid_offset = (self._grid_offset_x, self._grid_offset_y)
|
|
1846
1884
|
self.library.save(path, base_path=path.parent)
|
|
1847
1885
|
self._last_saved_path = path
|
|
1848
1886
|
print(f"Animations saved to {path}")
|
|
@@ -1888,6 +1926,7 @@ class SpriteAnimationEditor:
|
|
|
1888
1926
|
if self._last_saved_path:
|
|
1889
1927
|
# Save to existing path
|
|
1890
1928
|
try:
|
|
1929
|
+
self.library.grid_offset = (self._grid_offset_x, self._grid_offset_y)
|
|
1891
1930
|
self.library.save(
|
|
1892
1931
|
self._last_saved_path,
|
|
1893
1932
|
base_path=self._last_saved_path.parent,
|
|
@@ -1913,6 +1952,7 @@ class SpriteAnimationEditor:
|
|
|
1913
1952
|
try:
|
|
1914
1953
|
self.library = AnimationLibrary.load(path)
|
|
1915
1954
|
self._resolve_library_paths(path)
|
|
1955
|
+
self._apply_library_grid_settings()
|
|
1916
1956
|
names = self.library.animation_names()
|
|
1917
1957
|
self._active_anim_name = names[0] if names else None
|
|
1918
1958
|
if not names:
|
|
@@ -1948,6 +1988,8 @@ class SpriteAnimationEditor:
|
|
|
1948
1988
|
def _on_save_file_selected(self, path: Path) -> None:
|
|
1949
1989
|
"""Callback when user selects a file to save to."""
|
|
1950
1990
|
try:
|
|
1991
|
+
# Sync current grid offset into library before saving
|
|
1992
|
+
self.library.grid_offset = (self._grid_offset_x, self._grid_offset_y)
|
|
1951
1993
|
self.library.save(path, base_path=path.parent)
|
|
1952
1994
|
self._last_saved_path = path # Track for quick save
|
|
1953
1995
|
print(f"Animations saved to {path}")
|
|
@@ -1968,6 +2010,7 @@ class SpriteAnimationEditor:
|
|
|
1968
2010
|
self.library = AnimationLibrary.load(path)
|
|
1969
2011
|
self._resolve_library_paths(path)
|
|
1970
2012
|
self._last_saved_path = path # Track loaded file as save location
|
|
2013
|
+
self._apply_library_grid_settings()
|
|
1971
2014
|
names = self.library.animation_names()
|
|
1972
2015
|
self._active_anim_name = names[0] if names else None
|
|
1973
2016
|
if not names:
|
|
@@ -2110,6 +2153,7 @@ class SpriteAnimationEditor:
|
|
|
2110
2153
|
def load_animation_data(self, data: dict) -> None:
|
|
2111
2154
|
"""Load animation library from a dict (e.g. from project JSON)."""
|
|
2112
2155
|
self.library = AnimationLibrary.from_dict(data)
|
|
2156
|
+
self._apply_library_grid_settings()
|
|
2113
2157
|
names = self.library.animation_names()
|
|
2114
2158
|
self._active_anim_name = names[0] if names else None
|
|
2115
2159
|
if not names:
|
|
@@ -167,6 +167,7 @@ class AnimationLibrary:
|
|
|
167
167
|
animations: Dict[str, Animation] = field(default_factory=dict)
|
|
168
168
|
spritesheet_path: Optional[str] = None
|
|
169
169
|
tile_size: Tuple[int, int] = (32, 32)
|
|
170
|
+
grid_offset: Tuple[int, int] = (0, 0)
|
|
170
171
|
|
|
171
172
|
def add_animation(self, anim: Animation) -> None:
|
|
172
173
|
self.animations[anim.name] = anim
|
|
@@ -199,6 +200,7 @@ class AnimationLibrary:
|
|
|
199
200
|
return {
|
|
200
201
|
"spritesheet_path": spritesheet_path,
|
|
201
202
|
"tile_size": list(self.tile_size),
|
|
203
|
+
"grid_offset": list(self.grid_offset),
|
|
202
204
|
"animations": {
|
|
203
205
|
name: anim.to_dict() for name, anim in self.animations.items()
|
|
204
206
|
},
|
|
@@ -209,6 +211,7 @@ class AnimationLibrary:
|
|
|
209
211
|
lib = AnimationLibrary(
|
|
210
212
|
spritesheet_path=data.get("spritesheet_path"),
|
|
211
213
|
tile_size=tuple(data.get("tile_size", [32, 32])),
|
|
214
|
+
grid_offset=tuple(data.get("grid_offset", [0, 0])),
|
|
212
215
|
)
|
|
213
216
|
for name, anim_data in data.get("animations", {}).items():
|
|
214
217
|
lib.animations[name] = Animation.from_dict(anim_data)
|
|
@@ -140,6 +140,10 @@ class Timeline:
|
|
|
140
140
|
self.tile_size = tile_size
|
|
141
141
|
self._thumb_cache.clear()
|
|
142
142
|
|
|
143
|
+
def invalidate_cache(self) -> None:
|
|
144
|
+
"""Clear the thumbnail cache. Call after external changes to surface, tile_size, or grid offset."""
|
|
145
|
+
self._thumb_cache.clear()
|
|
146
|
+
|
|
143
147
|
def resize(self, rect: Rect) -> None:
|
|
144
148
|
self.rect = rect
|
|
145
149
|
|
|
@@ -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
|