tilemap-editor 3.3.10__tar.gz → 4.0.0__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.3.10/src/tilemap_editor.egg-info → tilemap_editor-4.0.0}/PKG-INFO +1 -1
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/pyproject.toml +1 -1
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/editor.py +104 -52
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/sprite_animation/preview.py +11 -64
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/tileset_collision/editor.py +1 -1
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap.py +13 -12
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/settings.py +2 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0/src/tilemap_editor.egg-info}/PKG-INFO +1 -1
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor.egg-info/SOURCES.txt +7 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/utils/serialization.py +12 -6
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/filemanager.py +85 -186
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/input.py +198 -92
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/layer_selector.py +40 -55
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/map_properties.py +39 -44
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/mapsetup.py +72 -56
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/tile_grid.py +361 -165
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/tile_selector.py +46 -117
- tilemap_editor-4.0.0/src/widgets/ui/button.py +83 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/confirm_dialog.py +19 -46
- tilemap_editor-4.0.0/src/widgets/ui/dialog_base.py +70 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/draw_utils.py +11 -0
- tilemap_editor-4.0.0/src/widgets/ui/label.py +40 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/layer_type_dialog.py +23 -50
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/property_editor.py +116 -84
- tilemap_editor-4.0.0/src/widgets/ui/scrollbar.py +144 -0
- tilemap_editor-4.0.0/src/widgets/ui/sidebar_container.py +155 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/theme.py +9 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/tileset_type_dialog.py +35 -62
- tilemap_editor-4.0.0/src/widgets/widget_base.py +42 -0
- tilemap_editor-4.0.0/tests/test_serialization_regression.py +87 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/LICENSE +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/MANIFEST.in +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/README.md +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/arrow-down.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/check.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/checked.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/close.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/duplicate.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/error.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/file.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/filedead.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/fit.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/folder.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/grid.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/image.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/info.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/load.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/loop.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/onion.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/pan.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/pause.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/pencil.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/play.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/plus.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/radio.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/reset.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/save.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/skip-back.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/skip-forward.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/stop.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/tilemap.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/tileset.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/unchecked.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/warning.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/zoomin.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/zoomout.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/setup.cfg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/configs/themes.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/constants.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/event_map.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/layers.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/main.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/node_manager.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/nodes.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/__init__.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/character_collision/__init__.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/character_collision/editor.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/character_collision/models.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/character_collision/protocols.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/character_collision/shape_editor.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/character_collision/standalone.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/object_tileset_collision/__init__.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/object_tileset_collision/editor.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/object_tileset_collision/models.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/object_tileset_collision/protocols.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/object_tileset_collision/standalone.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/sprite_animation/__init__.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/sprite_animation/__main__.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/sprite_animation/clipboard_util.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/sprite_animation/editor.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/sprite_animation/frame_picker.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/sprite_animation/models.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/sprite_animation/protocols.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/sprite_animation/runtime_load.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/sprite_animation/standalone.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/sprite_animation/timeline.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/sprite_animation/validation.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/sprite_editor/__init__.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/sprite_editor/editor.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/sprite_editor/standalone.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/tileset_collision/__init__.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/tileset_collision/collision_painter.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/tileset_collision/models.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/tileset_collision/protocols.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/tileset_collision/standalone.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/standalone_automap.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/standalone_error_console.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/standalone_filemanager.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/standalone_image_viewer.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/__init__.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/__main__.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/__init__.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/ToolMove.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/arrow-down.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/check.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/checked.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/close.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/duplicate.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/error.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/file.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/filedead.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/fit.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/folder.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/grid.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/image.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/info.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/load.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/loop.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/onion.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/pan.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/pause.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/pencil.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/play.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/plus.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/radio.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/reset.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/save.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/skip-back.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/skip-forward.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/stop.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/tilemap.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/tileset.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/unchecked.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/warning.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/zoomin.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/zoomout.svg +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/cli.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/main.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor.egg-info/dependency_links.txt +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor.egg-info/entry_points.txt +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor.egg-info/requires.txt +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor.egg-info/top_level.txt +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/ttypes/__init__.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/ttypes/tilemap.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/utils/__init__.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/utils/editor_preference.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/utils/error_handler.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/utils/font_manager.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/utils/history.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/utils/icon_manager.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/utils/icons_cache.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/utils/log_capture.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/utils/project_paths.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/utils/standalone.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/utils/validation.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/__init__.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/automap_models.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/autotile_template.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/autotiler.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/particle_presets.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/particle_system.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/regex_automap_designer.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/spritesheet_grid.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/checkbox.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/collision_layer_mask.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/collision_layer_sidebar.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/fileinput.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/menubar.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/mode_indicator.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/node_editor.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/node_selector.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/notification.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/particle_config_dialog.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/region_selector.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/status_bar.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/toolbar.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/tooltip.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/tests/test_animation_library_grid_offset.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/tests/test_collision_layer_mask.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/tests/test_collision_layer_sidebar.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/tests/test_editor_pan_mode.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/tests/test_project_paths.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/tests/test_render_scale.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/tests/test_sprite_animation_editor_grid.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/tests/test_tile_grid_selection.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/tests/test_tile_selector_pick.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/tests/test_tilemap_save.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/tests/test_timeline_invalidate_cache.py +0 -0
- {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/tests/test_toolbar_tools.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tilemap-editor"
|
|
7
|
-
version = "
|
|
7
|
+
version = "4.0.0"
|
|
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"
|
|
@@ -37,7 +37,8 @@ from widgets.ui.notification import NotificationManager
|
|
|
37
37
|
from widgets.ui.property_editor import PropertyEditor
|
|
38
38
|
from widgets.ui.particle_config_dialog import ParticleConfigDialog
|
|
39
39
|
from widgets.ui.tooltip import TooltipManager
|
|
40
|
-
from widgets.ui.theme import get_theme_manager, set_theme, THEMES
|
|
40
|
+
from widgets.ui.theme import COLORS, get_theme_manager, set_theme, THEMES
|
|
41
|
+
from widgets.ui.sidebar_container import SidebarContainer, ToolbarAction
|
|
41
42
|
from utils.log_capture import setup_console_log
|
|
42
43
|
from utils.standalone import launch_standalone
|
|
43
44
|
from utils import error_handler, error_context
|
|
@@ -148,6 +149,11 @@ class Editor:
|
|
|
148
149
|
logger.info(f"Project base_path: {self.base_path}")
|
|
149
150
|
logger.info(f"Data root: {self.data_root}")
|
|
150
151
|
|
|
152
|
+
theme_name = self.config.get("theme", "dark")
|
|
153
|
+
if theme_name in THEMES:
|
|
154
|
+
set_theme(theme_name)
|
|
155
|
+
logger.info(f"Applied theme: {theme_name}")
|
|
156
|
+
|
|
151
157
|
pygame.init()
|
|
152
158
|
pygame.display.set_caption("Pure Pygame Editor")
|
|
153
159
|
|
|
@@ -174,9 +180,8 @@ class Editor:
|
|
|
174
180
|
self.tilemap = Tilemap(self)
|
|
175
181
|
|
|
176
182
|
self.selector_w = 300
|
|
183
|
+
self._tileset_dragging = False
|
|
177
184
|
self.left_panel_w = 380 # Width for the dockable left sidebar
|
|
178
|
-
self.tileset_h = 300
|
|
179
|
-
self.layer_h = 150
|
|
180
185
|
self.map_setup_widget: Optional[MapSetup] = None
|
|
181
186
|
self.map_properties_dialog: Optional[MapPropertiesDialog] = None
|
|
182
187
|
self.tileset_widget: Optional[TileSelector] = None
|
|
@@ -225,16 +230,20 @@ class Editor:
|
|
|
225
230
|
self.tilemap.init_size((32, 32), (50, 50))
|
|
226
231
|
|
|
227
232
|
menu_h = 30
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
)
|
|
231
|
-
self.
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
self.
|
|
236
|
-
self.
|
|
237
|
-
|
|
233
|
+
toolbar_h = 35
|
|
234
|
+
self.tileset_widget = TileSelector(self, 0, 0, self.selector_w, 100)
|
|
235
|
+
self.layer_widget = LayerSelector(self, 0, 0, self.selector_w, 100)
|
|
236
|
+
sidebar_x = self.width - self.selector_w
|
|
237
|
+
sidebar_h = self.height - menu_h - toolbar_h
|
|
238
|
+
tileset_actions = [
|
|
239
|
+
ToolbarAction("E", self.tileset_widget._export_selected_as_png_dialog, "E: Export selection as PNG"),
|
|
240
|
+
ToolbarAction("C", self.tileset_widget.open_collision_editor, "Edit Collision Shapes"),
|
|
241
|
+
ToolbarAction("+", self.tileset_widget.request_add_tileset, "Add Tileset"),
|
|
242
|
+
ToolbarAction("-", self.tileset_widget.remove_tileset, "Remove Tileset"),
|
|
243
|
+
]
|
|
244
|
+
self.sidebar = SidebarContainer(self, Rect(sidebar_x, menu_h + toolbar_h, self.selector_w, sidebar_h))
|
|
245
|
+
self.sidebar.add_tab("Tilesets", self.tileset_widget, tileset_actions)
|
|
246
|
+
self.sidebar.add_tab("Layers", self.layer_widget)
|
|
238
247
|
self.tile_grid_widget = TileGrid(
|
|
239
248
|
self,
|
|
240
249
|
Rect(0, menu_h, self.width - self.selector_w, self.height - menu_h - 25),
|
|
@@ -557,15 +566,45 @@ class Editor:
|
|
|
557
566
|
return
|
|
558
567
|
|
|
559
568
|
try:
|
|
560
|
-
self.
|
|
569
|
+
self.handle_resize(self.width, self.height)
|
|
561
570
|
self.tilemap.apply_map_payload(path, payload_or_error)
|
|
562
571
|
except Exception as e:
|
|
563
572
|
error_handler.capture(e, context="load_map_apply")
|
|
573
|
+
import traceback
|
|
574
|
+
traceback.print_exc()
|
|
564
575
|
finally:
|
|
565
576
|
self.loading_state["active"] = False
|
|
566
577
|
|
|
567
578
|
def post_map_setup(self):
|
|
568
579
|
self.handle_resize(self.width, self.height)
|
|
580
|
+
if hasattr(self, "tile_grid_widget") and self.tile_grid_widget:
|
|
581
|
+
self.tile_grid_widget.center_on_map()
|
|
582
|
+
|
|
583
|
+
def _update_side_panel_layout(self):
|
|
584
|
+
menu_h = 30
|
|
585
|
+
toolbar_h = 35
|
|
586
|
+
left_offset = self.left_panel_w if self.left_panel_visible else 0
|
|
587
|
+
|
|
588
|
+
min_canvas_w = 200
|
|
589
|
+
available_w = self.width - left_offset
|
|
590
|
+
self._effective_selector_w = min(self.selector_w, max(0, available_w - min_canvas_w))
|
|
591
|
+
|
|
592
|
+
if hasattr(self, "sidebar") and self.sidebar:
|
|
593
|
+
self.sidebar.resize(
|
|
594
|
+
self.width - self._effective_selector_w,
|
|
595
|
+
menu_h + toolbar_h,
|
|
596
|
+
self._effective_selector_w,
|
|
597
|
+
self.height - menu_h - toolbar_h,
|
|
598
|
+
)
|
|
599
|
+
|
|
600
|
+
if hasattr(self, "tile_grid_widget") and self.tile_grid_widget:
|
|
601
|
+
canvas_w = available_w - self._effective_selector_w
|
|
602
|
+
self.tile_grid_widget.rect = Rect(
|
|
603
|
+
left_offset,
|
|
604
|
+
menu_h + toolbar_h,
|
|
605
|
+
canvas_w,
|
|
606
|
+
self.height - (menu_h + toolbar_h) - 25,
|
|
607
|
+
)
|
|
569
608
|
|
|
570
609
|
def handle_resize(self, width: int, height: int):
|
|
571
610
|
self.width = width
|
|
@@ -574,38 +613,13 @@ class Editor:
|
|
|
574
613
|
menu_h = 30
|
|
575
614
|
toolbar_h = 35
|
|
576
615
|
|
|
577
|
-
# Left sidebar offset
|
|
578
|
-
left_offset = self.left_panel_w if self.left_panel_visible else 0
|
|
579
|
-
|
|
580
616
|
if hasattr(self, "menubar") and self.menubar:
|
|
581
617
|
self.menubar.resize(width)
|
|
582
618
|
|
|
583
619
|
if hasattr(self, "toolbar") and self.toolbar:
|
|
584
620
|
self.toolbar.resize(width)
|
|
585
621
|
|
|
586
|
-
|
|
587
|
-
self.tileset_widget.resize(
|
|
588
|
-
width - self.selector_w - left_offset,
|
|
589
|
-
menu_h + toolbar_h,
|
|
590
|
-
self.selector_w,
|
|
591
|
-
self.tileset_h,
|
|
592
|
-
)
|
|
593
|
-
|
|
594
|
-
if hasattr(self, "layer_widget") and self.layer_widget:
|
|
595
|
-
self.layer_widget.resize(
|
|
596
|
-
width - self.selector_w - left_offset,
|
|
597
|
-
menu_h + toolbar_h + self.tileset_h,
|
|
598
|
-
self.selector_w,
|
|
599
|
-
height - (menu_h + toolbar_h + self.tileset_h),
|
|
600
|
-
)
|
|
601
|
-
|
|
602
|
-
if hasattr(self, "tile_grid_widget") and self.tile_grid_widget:
|
|
603
|
-
self.tile_grid_widget.rect = Rect(
|
|
604
|
-
left_offset,
|
|
605
|
-
menu_h + toolbar_h,
|
|
606
|
-
width - self.selector_w - left_offset,
|
|
607
|
-
height - (menu_h + toolbar_h),
|
|
608
|
-
)
|
|
622
|
+
self._update_side_panel_layout()
|
|
609
623
|
|
|
610
624
|
# Update animation panel rect
|
|
611
625
|
if self.left_panel_visible and self.animation_panel:
|
|
@@ -673,6 +687,7 @@ class Editor:
|
|
|
673
687
|
self.left_panel_visible = True
|
|
674
688
|
else:
|
|
675
689
|
self.left_panel_visible = False
|
|
690
|
+
self.handle_resize(self.width, self.height)
|
|
676
691
|
|
|
677
692
|
|
|
678
693
|
def _init_animation_panel(self):
|
|
@@ -713,9 +728,13 @@ class Editor:
|
|
|
713
728
|
|
|
714
729
|
def undo(self):
|
|
715
730
|
self.tilemap.undo()
|
|
731
|
+
if self.tile_grid_widget:
|
|
732
|
+
self.tile_grid_widget.invalidate_bounds_cache()
|
|
716
733
|
|
|
717
734
|
def redo(self):
|
|
718
735
|
self.tilemap.redo()
|
|
736
|
+
if self.tile_grid_widget:
|
|
737
|
+
self.tile_grid_widget.invalidate_bounds_cache()
|
|
719
738
|
|
|
720
739
|
def toggle_grid(self):
|
|
721
740
|
if self.tile_grid_widget:
|
|
@@ -730,6 +749,17 @@ class Editor:
|
|
|
730
749
|
new_theme = theme_names[next_idx]
|
|
731
750
|
set_theme(new_theme)
|
|
732
751
|
self.notifications.notify(f"Theme: {new_theme}")
|
|
752
|
+
|
|
753
|
+
settings_file = Path.cwd() / "settings.json"
|
|
754
|
+
if settings_file.exists():
|
|
755
|
+
try:
|
|
756
|
+
with open(settings_file, "r", encoding="utf-8") as f:
|
|
757
|
+
config = json.load(f)
|
|
758
|
+
config["theme"] = new_theme
|
|
759
|
+
with open(settings_file, "w", encoding="utf-8") as f:
|
|
760
|
+
json.dump(config, f, indent=4)
|
|
761
|
+
except Exception:
|
|
762
|
+
pass
|
|
733
763
|
|
|
734
764
|
def launch_external_automap(self):
|
|
735
765
|
if hasattr(self.autotiler, "_launch_external_viewer"):
|
|
@@ -1266,15 +1296,34 @@ class Editor:
|
|
|
1266
1296
|
if self.animation_panel.handle_event(event):
|
|
1267
1297
|
continue
|
|
1268
1298
|
|
|
1269
|
-
# Priority
|
|
1299
|
+
# Priority 6.5: Sidebar resize drag (vertical)
|
|
1300
|
+
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
|
|
1301
|
+
mx, my = event.pos
|
|
1302
|
+
edge_x = self.width - self._effective_selector_w
|
|
1303
|
+
if abs(mx - edge_x) < 6:
|
|
1304
|
+
self._tileset_dragging = True
|
|
1305
|
+
continue
|
|
1306
|
+
elif event.type == pygame.MOUSEMOTION:
|
|
1307
|
+
mx, my = event.pos
|
|
1308
|
+
edge_x = self.width - self._effective_selector_w
|
|
1309
|
+
if self._tileset_dragging:
|
|
1310
|
+
new_w = max(150, min(self.width // 2, self.width - mx))
|
|
1311
|
+
self.selector_w = new_w
|
|
1312
|
+
self._update_side_panel_layout()
|
|
1313
|
+
continue
|
|
1314
|
+
near_vertical = abs(mx - edge_x) < 6
|
|
1315
|
+
if near_vertical:
|
|
1316
|
+
pygame.mouse.set_cursor(pygame.SYSTEM_CURSOR_SIZEWE)
|
|
1317
|
+
else:
|
|
1318
|
+
pygame.mouse.set_cursor(pygame.SYSTEM_CURSOR_ARROW)
|
|
1319
|
+
elif event.type == pygame.MOUSEBUTTONUP and event.button == 1:
|
|
1320
|
+
if self._tileset_dragging:
|
|
1321
|
+
self._tileset_dragging = False
|
|
1322
|
+
continue
|
|
1323
|
+
|
|
1324
|
+
# Priority 7: Sidebar container
|
|
1270
1325
|
consumed = False
|
|
1271
|
-
if self.
|
|
1272
|
-
consumed = True
|
|
1273
|
-
if (
|
|
1274
|
-
not consumed
|
|
1275
|
-
and self.tileset_widget
|
|
1276
|
-
and self.tileset_widget.handle_event(event)
|
|
1277
|
-
):
|
|
1326
|
+
if self.sidebar and self.sidebar.handle_event(event):
|
|
1278
1327
|
consumed = True
|
|
1279
1328
|
|
|
1280
1329
|
# Priority 8: Main tile grid (lowest priority)
|
|
@@ -1306,10 +1355,13 @@ class Editor:
|
|
|
1306
1355
|
|
|
1307
1356
|
if self.tile_grid_widget:
|
|
1308
1357
|
self.tile_grid_widget.draw(self.screen)
|
|
1309
|
-
if self.
|
|
1310
|
-
self.
|
|
1311
|
-
|
|
1312
|
-
|
|
1358
|
+
if self.sidebar:
|
|
1359
|
+
self.sidebar.draw(self.screen)
|
|
1360
|
+
edge_x = self.width - self._effective_selector_w
|
|
1361
|
+
handle_rect = Rect(edge_x - 1, 0, 3, self.height)
|
|
1362
|
+
pygame.draw.rect(self.screen, COLORS.border, handle_rect)
|
|
1363
|
+
if self._tileset_dragging:
|
|
1364
|
+
pygame.draw.rect(self.screen, COLORS.accent, handle_rect)
|
|
1313
1365
|
|
|
1314
1366
|
# Draw autotiler and regex designer with modal overlay
|
|
1315
1367
|
if self.autotiler:
|
|
@@ -12,11 +12,11 @@ from typing import Callable, List, Optional, Tuple
|
|
|
12
12
|
import pygame
|
|
13
13
|
from pygame import Rect
|
|
14
14
|
|
|
15
|
-
from .
|
|
16
|
-
from utils.font_manager import font_manager, FontWeight, FontStyle
|
|
15
|
+
from utils.font_manager import FontStyle, FontWeight, font_manager
|
|
17
16
|
from utils.icon_manager import icon_manager
|
|
18
17
|
|
|
19
|
-
|
|
18
|
+
from .models import AnimationFrame
|
|
19
|
+
|
|
20
20
|
_COLORS = {
|
|
21
21
|
"bg": (25, 27, 30),
|
|
22
22
|
"panel": (35, 38, 44),
|
|
@@ -39,7 +39,6 @@ _COLORS = {
|
|
|
39
39
|
|
|
40
40
|
HEADER_H = 36
|
|
41
41
|
CONTROLS_H = 32
|
|
42
|
-
# Preview timing vs a 60 Hz editor tick: elapsed += dt_ms * (playback_fps / 60).
|
|
43
42
|
|
|
44
43
|
|
|
45
44
|
class AnimationPreview:
|
|
@@ -76,7 +75,6 @@ class AnimationPreview:
|
|
|
76
75
|
self._editing_fps = False
|
|
77
76
|
self._fps_input_rect = Rect(0, 0, 0, 0)
|
|
78
77
|
|
|
79
|
-
# Buttons (recalculated in draw)
|
|
80
78
|
self._btn_play = Rect(0, 0, 0, 0)
|
|
81
79
|
self._btn_stop = Rect(0, 0, 0, 0)
|
|
82
80
|
self._btn_loop = Rect(0, 0, 0, 0)
|
|
@@ -84,24 +82,21 @@ class AnimationPreview:
|
|
|
84
82
|
self._btn_prev = Rect(0, 0, 0, 0)
|
|
85
83
|
self._btn_next = Rect(0, 0, 0, 0)
|
|
86
84
|
|
|
87
|
-
# Scrubber fraction output (read by timeline)
|
|
88
85
|
self.scrubber_frac: float = 0.0
|
|
89
86
|
|
|
90
87
|
self._font: Optional[pygame.font.Font] = None
|
|
91
88
|
self._font_sm: Optional[pygame.font.Font] = None
|
|
92
89
|
self._checker: Optional[pygame.Surface] = None
|
|
93
90
|
|
|
94
|
-
# ------------------------------------------------------------------
|
|
95
|
-
# Public
|
|
96
|
-
# ------------------------------------------------------------------
|
|
97
|
-
|
|
98
91
|
def set_frames(self, frames: List[AnimationFrame]) -> None:
|
|
99
92
|
self.frames = frames
|
|
100
93
|
self.current_frame = 0
|
|
101
94
|
self._elapsed = 0.0
|
|
102
95
|
|
|
103
96
|
def set_surface(
|
|
104
|
-
self,
|
|
97
|
+
self,
|
|
98
|
+
surface: pygame.Surface,
|
|
99
|
+
tile_size: Optional[Tuple[int, int]] = None
|
|
105
100
|
) -> None:
|
|
106
101
|
self.surface = surface
|
|
107
102
|
if tile_size:
|
|
@@ -154,10 +149,6 @@ class AnimationPreview:
|
|
|
154
149
|
self._elapsed = 0.0
|
|
155
150
|
self.scrubber_frac = 0.0
|
|
156
151
|
|
|
157
|
-
# ------------------------------------------------------------------
|
|
158
|
-
# Update (call every frame with dt in milliseconds)
|
|
159
|
-
# ------------------------------------------------------------------
|
|
160
|
-
|
|
161
152
|
def update(self, dt_ms: float) -> None:
|
|
162
153
|
if not self.playing or not self.frames:
|
|
163
154
|
return
|
|
@@ -178,7 +169,6 @@ class AnimationPreview:
|
|
|
178
169
|
break
|
|
179
170
|
cur = self.frames[self.current_frame]
|
|
180
171
|
|
|
181
|
-
# Compute scrubber fraction
|
|
182
172
|
total = sum(f.duration_ms for f in self.frames)
|
|
183
173
|
if total > 0:
|
|
184
174
|
elapsed_to_cur = (
|
|
@@ -189,21 +179,13 @@ class AnimationPreview:
|
|
|
189
179
|
else:
|
|
190
180
|
self.scrubber_frac = 0.0
|
|
191
181
|
|
|
192
|
-
# ------------------------------------------------------------------
|
|
193
|
-
# Events
|
|
194
|
-
# ------------------------------------------------------------------
|
|
195
|
-
|
|
196
182
|
def handle_event(self, event: pygame.event.Event) -> bool:
|
|
197
183
|
if event.type == pygame.KEYDOWN and self._editing_fps:
|
|
198
184
|
return self._handle_fps_keydown(event)
|
|
199
185
|
|
|
200
186
|
mouse = pygame.mouse.get_pos()
|
|
201
187
|
if not self.rect.collidepoint(mouse):
|
|
202
|
-
if
|
|
203
|
-
event.type == pygame.MOUSEBUTTONDOWN
|
|
204
|
-
and event.button == 1
|
|
205
|
-
and self._editing_fps
|
|
206
|
-
):
|
|
188
|
+
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1 and self._editing_fps:
|
|
207
189
|
self.commit_fps_input()
|
|
208
190
|
return False
|
|
209
191
|
|
|
@@ -237,7 +219,6 @@ class AnimationPreview:
|
|
|
237
219
|
self._step(1)
|
|
238
220
|
return True
|
|
239
221
|
|
|
240
|
-
# Scroll to adjust zoom
|
|
241
222
|
if event.type == pygame.MOUSEWHEEL and self.rect.collidepoint(mouse):
|
|
242
223
|
self._zoom *= 1.15 if event.y > 0 else 0.87
|
|
243
224
|
self._zoom = max(1.0, min(self._zoom, 12.0))
|
|
@@ -245,10 +226,6 @@ class AnimationPreview:
|
|
|
245
226
|
|
|
246
227
|
return False
|
|
247
228
|
|
|
248
|
-
# ------------------------------------------------------------------
|
|
249
|
-
# Drawing
|
|
250
|
-
# ------------------------------------------------------------------
|
|
251
|
-
|
|
252
229
|
def draw(self, screen: pygame.Surface) -> None:
|
|
253
230
|
self._ensure_fonts()
|
|
254
231
|
clip = screen.get_clip()
|
|
@@ -257,15 +234,12 @@ class AnimationPreview:
|
|
|
257
234
|
pygame.draw.rect(screen, _COLORS["panel"], self.rect)
|
|
258
235
|
pygame.draw.rect(screen, _COLORS["border"], self.rect, 1)
|
|
259
236
|
|
|
260
|
-
# Header
|
|
261
237
|
hdr = Rect(self.rect.x, self.rect.y, self.rect.w, HEADER_H)
|
|
262
238
|
pygame.draw.rect(screen, _COLORS["header"], hdr)
|
|
263
239
|
title = "Preview"
|
|
264
240
|
if self.frames:
|
|
265
241
|
title += f" {self.current_frame + 1}/{len(self.frames)}"
|
|
266
|
-
screen.blit(
|
|
267
|
-
self._font.render(title, True, _COLORS["text"]), (hdr.x + 6, hdr.y + 2)
|
|
268
|
-
)
|
|
242
|
+
screen.blit(self._font.render(title, True, _COLORS["text"]), (hdr.x + 6, hdr.y + 2))
|
|
269
243
|
if self.frames and 0 <= self.current_frame < len(self.frames):
|
|
270
244
|
fr = self.frames[self.current_frame]
|
|
271
245
|
auth = max(0.001, float(self.authoring_fps))
|
|
@@ -276,19 +250,15 @@ class AnimationPreview:
|
|
|
276
250
|
(hdr.x + 6, hdr.y + 18),
|
|
277
251
|
)
|
|
278
252
|
|
|
279
|
-
# Preview area
|
|
280
253
|
preview_y = self.rect.y + HEADER_H
|
|
281
254
|
preview_h = self.rect.h - HEADER_H - CONTROLS_H
|
|
282
255
|
preview_rect = Rect(self.rect.x, preview_y, self.rect.w, preview_h)
|
|
283
256
|
|
|
284
|
-
# Checkerboard background
|
|
285
257
|
self._draw_checker(screen, preview_rect)
|
|
286
258
|
|
|
287
|
-
# Draw current frame
|
|
288
259
|
if self.frames and 0 <= self.current_frame < len(self.frames):
|
|
289
260
|
frame = self.frames[self.current_frame]
|
|
290
261
|
|
|
291
|
-
# Onion skin (previous frame)
|
|
292
262
|
if self.show_onion and self.current_frame > 0:
|
|
293
263
|
prev_frame = self.frames[self.current_frame - 1]
|
|
294
264
|
prev_surf = self._extract_tile(prev_frame.variant_id)
|
|
@@ -305,7 +275,6 @@ class AnimationPreview:
|
|
|
305
275
|
dy = preview_rect.centery - zoomed.get_height() // 2
|
|
306
276
|
screen.blit(zoomed, (dx, dy))
|
|
307
277
|
|
|
308
|
-
# Current frame
|
|
309
278
|
tile_surf = self._extract_tile(frame.variant_id)
|
|
310
279
|
if tile_surf:
|
|
311
280
|
zw = int(self.tile_size[0] * self._zoom)
|
|
@@ -319,7 +288,6 @@ class AnimationPreview:
|
|
|
319
288
|
no_anim = self._font.render("No frames", True, _COLORS["text_dim"])
|
|
320
289
|
screen.blit(no_anim, no_anim.get_rect(center=preview_rect.center))
|
|
321
290
|
|
|
322
|
-
# Controls bar
|
|
323
291
|
ctrl_y = self.rect.bottom - CONTROLS_H
|
|
324
292
|
ctrl_rect = Rect(self.rect.x, ctrl_y, self.rect.w, CONTROLS_H)
|
|
325
293
|
pygame.draw.rect(screen, _COLORS["header"], ctrl_rect)
|
|
@@ -328,10 +296,6 @@ class AnimationPreview:
|
|
|
328
296
|
|
|
329
297
|
screen.set_clip(clip)
|
|
330
298
|
|
|
331
|
-
# ------------------------------------------------------------------
|
|
332
|
-
# Controls drawing
|
|
333
|
-
# ------------------------------------------------------------------
|
|
334
|
-
|
|
335
299
|
def _draw_controls(self, screen: pygame.Surface, ctrl_rect: Rect) -> None:
|
|
336
300
|
mouse = pygame.mouse.get_pos()
|
|
337
301
|
bw, bh = 28, 24
|
|
@@ -339,53 +303,43 @@ class AnimationPreview:
|
|
|
339
303
|
x = ctrl_rect.x + pad
|
|
340
304
|
y = ctrl_rect.y + (ctrl_rect.h - bh) // 2
|
|
341
305
|
|
|
342
|
-
# |<< (prev)
|
|
343
306
|
self._btn_prev = Rect(x, y, bw, bh)
|
|
344
307
|
self._draw_btn(screen, self._btn_prev, "", mouse)
|
|
345
308
|
prev_icon = icon_manager.get_icon("skip-back", 12, _COLORS["text"])
|
|
346
309
|
screen.blit(prev_icon, prev_icon.get_rect(center=self._btn_prev.center))
|
|
347
310
|
x += bw + pad
|
|
348
311
|
|
|
349
|
-
# Play / Pause
|
|
350
312
|
self._btn_play = Rect(x, y, bw, bh)
|
|
351
313
|
self._draw_btn(screen, self._btn_play, "", mouse, active=self.playing)
|
|
352
|
-
play_icon = icon_manager.get_icon(
|
|
353
|
-
"pause" if self.playing else "play", 12, _COLORS["text"]
|
|
354
|
-
)
|
|
314
|
+
play_icon = icon_manager.get_icon("pause" if self.playing else "play", 12, _COLORS["text"])
|
|
355
315
|
screen.blit(play_icon, play_icon.get_rect(center=self._btn_play.center))
|
|
356
316
|
x += bw + pad
|
|
357
317
|
|
|
358
|
-
# Stop
|
|
359
318
|
self._btn_stop = Rect(x, y, bw, bh)
|
|
360
319
|
self._draw_btn(screen, self._btn_stop, "", mouse)
|
|
361
320
|
stop_icon = icon_manager.get_icon("stop", 12, _COLORS["text"])
|
|
362
321
|
screen.blit(stop_icon, stop_icon.get_rect(center=self._btn_stop.center))
|
|
363
322
|
x += bw + pad
|
|
364
323
|
|
|
365
|
-
# >>| (next)
|
|
366
324
|
self._btn_next = Rect(x, y, bw, bh)
|
|
367
325
|
self._draw_btn(screen, self._btn_next, "", mouse)
|
|
368
326
|
next_icon = icon_manager.get_icon("skip-forward", 12, _COLORS["text"])
|
|
369
327
|
screen.blit(next_icon, next_icon.get_rect(center=self._btn_next.center))
|
|
370
328
|
x += bw + pad + 4
|
|
371
329
|
|
|
372
|
-
# Loop toggle
|
|
373
330
|
self._btn_loop = Rect(x, y, bw + 4, bh)
|
|
374
331
|
self._draw_btn(screen, self._btn_loop, "", mouse, active=self.loop)
|
|
375
332
|
loop_icon = icon_manager.get_icon("loop", 12, _COLORS["text"])
|
|
376
333
|
screen.blit(loop_icon, loop_icon.get_rect(center=self._btn_loop.center))
|
|
377
334
|
x += bw + pad + 8
|
|
378
335
|
|
|
379
|
-
# Playback FPS (editable; timing vs 60 Hz tick — see update())
|
|
380
336
|
fps_tag = self._font_sm.render("FPS", True, _COLORS["text_dim"])
|
|
381
337
|
screen.blit(fps_tag, (x, y + (bh - fps_tag.get_height()) // 2))
|
|
382
338
|
x += fps_tag.get_width() + 3
|
|
383
339
|
self._fps_input_rect = Rect(x, y, 44, bh)
|
|
384
340
|
fps_bg = _COLORS["btn_active"] if self._editing_fps else _COLORS["input_bg"]
|
|
385
341
|
pygame.draw.rect(screen, fps_bg, self._fps_input_rect, border_radius=3)
|
|
386
|
-
pygame.draw.rect(
|
|
387
|
-
screen, _COLORS["border"], self._fps_input_rect, 1, border_radius=3
|
|
388
|
-
)
|
|
342
|
+
pygame.draw.rect(screen, _COLORS["border"], self._fps_input_rect, 1, border_radius=3)
|
|
389
343
|
shown = self._fps_input_text
|
|
390
344
|
if self._editing_fps and (pygame.time.get_ticks() // 400) % 2:
|
|
391
345
|
shown += "|"
|
|
@@ -394,16 +348,13 @@ class AnimationPreview:
|
|
|
394
348
|
screen.blit(fps_surf, (self._fps_input_rect.x + 4, self._fps_input_rect.y + 5))
|
|
395
349
|
x += 44 + pad
|
|
396
350
|
|
|
397
|
-
# Onion skin
|
|
398
351
|
if x + bw + 12 < ctrl_rect.right:
|
|
399
352
|
self._btn_onion = Rect(x, y, bw + 4, bh)
|
|
400
353
|
self._draw_btn(screen, self._btn_onion, "", mouse, active=self.show_onion)
|
|
401
354
|
onion_icon = icon_manager.get_icon("onion", 14, _COLORS["text"])
|
|
402
355
|
screen.blit(onion_icon, onion_icon.get_rect(center=self._btn_onion.center))
|
|
403
356
|
|
|
404
|
-
def _draw_btn(
|
|
405
|
-
self, screen: pygame.Surface, rect: Rect, label: str, mouse, active=False
|
|
406
|
-
) -> None:
|
|
357
|
+
def _draw_btn(self, screen: pygame.Surface, rect: Rect, label: str, mouse, active=False) -> None:
|
|
407
358
|
hover = rect.collidepoint(mouse)
|
|
408
359
|
if active:
|
|
409
360
|
bg = _COLORS["btn_active"]
|
|
@@ -416,10 +367,6 @@ class AnimationPreview:
|
|
|
416
367
|
lbl = self._font_sm.render(label, True, _COLORS["text"])
|
|
417
368
|
screen.blit(lbl, lbl.get_rect(center=rect.center))
|
|
418
369
|
|
|
419
|
-
# ------------------------------------------------------------------
|
|
420
|
-
# Helpers
|
|
421
|
-
# ------------------------------------------------------------------
|
|
422
|
-
|
|
423
370
|
def _extract_tile(self, variant_id: int) -> Optional[pygame.Surface]:
|
|
424
371
|
tw, th = self.tile_size
|
|
425
372
|
ox = self.grid_offset_x
|
|
@@ -625,7 +625,7 @@ class TilesetCollisionEditor:
|
|
|
625
625
|
"""Save collision data to file, auto-propagating within auto-tile groups."""
|
|
626
626
|
try:
|
|
627
627
|
self._save_tile_collision_for_selection()
|
|
628
|
-
self._propagate_collision_to_groups()
|
|
628
|
+
# self._propagate_collision_to_groups() # disabled — auto-copies collision to all autotile variants, breaks per-variant shapes
|
|
629
629
|
self.library.save(path)
|
|
630
630
|
except Exception as e:
|
|
631
631
|
error_handler.capture(e, context="save_collision_file")
|
|
@@ -477,8 +477,7 @@ class Tilemap:
|
|
|
477
477
|
try:
|
|
478
478
|
self.tile_size = deserialize_point(payload["meta"]["tile_size"])
|
|
479
479
|
self.map_size = deserialize_point(payload["meta"].get("map_size", "50;50"))
|
|
480
|
-
|
|
481
|
-
# But the 'initial' setup might be in meta or we use map_size as base
|
|
480
|
+
|
|
482
481
|
self.initial_map_size = payload["meta"].get("initial_map_size")
|
|
483
482
|
if self.initial_map_size:
|
|
484
483
|
self.initial_map_size = deserialize_point(self.initial_map_size)
|
|
@@ -488,16 +487,6 @@ class Tilemap:
|
|
|
488
487
|
except (KeyError, ValueError) as e:
|
|
489
488
|
raise ValueError(f"Error loading map metadata: {e}") from e
|
|
490
489
|
|
|
491
|
-
# Restore view state
|
|
492
|
-
if self.editor.tile_grid_widget:
|
|
493
|
-
if "zoom_level" in payload["meta"]:
|
|
494
|
-
self.editor.tile_grid_widget.zoom_level = payload["meta"]["zoom_level"]
|
|
495
|
-
if "scroll" in payload["meta"]:
|
|
496
|
-
scroll = deserialize_point(payload["meta"]["scroll"])
|
|
497
|
-
self.editor.tile_grid_widget.scroll_x = scroll[0]
|
|
498
|
-
self.editor.tile_grid_widget.scroll_y = scroll[1]
|
|
499
|
-
self.editor.tile_grid_widget.clamp_view()
|
|
500
|
-
|
|
501
490
|
resources = payload.get("resources", {})
|
|
502
491
|
tilesets = []
|
|
503
492
|
if isinstance(resources, list):
|
|
@@ -669,6 +658,18 @@ class Tilemap:
|
|
|
669
658
|
|
|
670
659
|
self.initialized = True
|
|
671
660
|
|
|
661
|
+
# Restore view state and invalidate bounds cache after map load
|
|
662
|
+
if self.editor.tile_grid_widget:
|
|
663
|
+
if "zoom_level" in payload["meta"]:
|
|
664
|
+
self.editor.tile_grid_widget.zoom_level = payload["meta"]["zoom_level"]
|
|
665
|
+
if "scroll" in payload["meta"]:
|
|
666
|
+
scroll = deserialize_point(payload["meta"]["scroll"])
|
|
667
|
+
self.editor.tile_grid_widget.scroll_x = scroll[0]
|
|
668
|
+
self.editor.tile_grid_widget.scroll_y = scroll[1]
|
|
669
|
+
if hasattr(self.editor.tile_grid_widget, "clamp_scroll"):
|
|
670
|
+
self.editor.tile_grid_widget.clamp_scroll()
|
|
671
|
+
self.editor.tile_grid_widget.invalidate_bounds_cache()
|
|
672
|
+
|
|
672
673
|
def _object_tileset_indices_from_payload(self, payload: dict) -> set[int]:
|
|
673
674
|
"""Infer legacy object tileset resources from object-layer references."""
|
|
674
675
|
indices: set[int] = set()
|
|
@@ -25,6 +25,8 @@ def init_settings(generate_main: bool = False) -> None:
|
|
|
25
25
|
"character": "character_collision"
|
|
26
26
|
},
|
|
27
27
|
"nodes_path": "nodes",
|
|
28
|
+
"theme": "dark",
|
|
29
|
+
"themes_list": ["dark", "molokai", "light", "semi_light"],
|
|
28
30
|
"error_handler": {
|
|
29
31
|
"log_path": "errors.log",
|
|
30
32
|
"max_recent_errors": 50,
|
|
@@ -172,12 +172,16 @@ src/widgets/regex_automap_designer.py
|
|
|
172
172
|
src/widgets/spritesheet_grid.py
|
|
173
173
|
src/widgets/tile_grid.py
|
|
174
174
|
src/widgets/tile_selector.py
|
|
175
|
+
src/widgets/widget_base.py
|
|
176
|
+
src/widgets/ui/button.py
|
|
175
177
|
src/widgets/ui/checkbox.py
|
|
176
178
|
src/widgets/ui/collision_layer_mask.py
|
|
177
179
|
src/widgets/ui/collision_layer_sidebar.py
|
|
178
180
|
src/widgets/ui/confirm_dialog.py
|
|
181
|
+
src/widgets/ui/dialog_base.py
|
|
179
182
|
src/widgets/ui/draw_utils.py
|
|
180
183
|
src/widgets/ui/fileinput.py
|
|
184
|
+
src/widgets/ui/label.py
|
|
181
185
|
src/widgets/ui/layer_type_dialog.py
|
|
182
186
|
src/widgets/ui/menubar.py
|
|
183
187
|
src/widgets/ui/mode_indicator.py
|
|
@@ -187,6 +191,8 @@ src/widgets/ui/notification.py
|
|
|
187
191
|
src/widgets/ui/particle_config_dialog.py
|
|
188
192
|
src/widgets/ui/property_editor.py
|
|
189
193
|
src/widgets/ui/region_selector.py
|
|
194
|
+
src/widgets/ui/scrollbar.py
|
|
195
|
+
src/widgets/ui/sidebar_container.py
|
|
190
196
|
src/widgets/ui/status_bar.py
|
|
191
197
|
src/widgets/ui/theme.py
|
|
192
198
|
src/widgets/ui/tileset_type_dialog.py
|
|
@@ -198,6 +204,7 @@ tests/test_collision_layer_sidebar.py
|
|
|
198
204
|
tests/test_editor_pan_mode.py
|
|
199
205
|
tests/test_project_paths.py
|
|
200
206
|
tests/test_render_scale.py
|
|
207
|
+
tests/test_serialization_regression.py
|
|
201
208
|
tests/test_sprite_animation_editor_grid.py
|
|
202
209
|
tests/test_tile_grid_selection.py
|
|
203
210
|
tests/test_tile_selector_pick.py
|