tilemap-editor 3.3.3__tar.gz → 3.3.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.3.3/src/tilemap_editor.egg-info → tilemap_editor-3.3.4}/PKG-INFO +1 -1
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/pyproject.toml +1 -1
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/sprite_editor/editor.py +24 -5
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/tileset_collision/collision_painter.py +137 -34
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/tileset_collision/editor.py +192 -2
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4/src/tilemap_editor.egg-info}/PKG-INFO +1 -1
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor.egg-info/SOURCES.txt +1 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/filemanager.py +2 -4
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/spritesheet_grid.py +190 -30
- tilemap_editor-3.3.4/src/widgets/ui/checkbox.py +99 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/LICENSE +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/MANIFEST.in +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/README.md +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/icons/arrow-down.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/icons/check.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/icons/checked.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/icons/close.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/icons/duplicate.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/icons/error.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/icons/file.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/icons/filedead.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/icons/fit.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/icons/folder.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/icons/grid.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/icons/image.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/icons/info.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/icons/load.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/icons/loop.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/icons/pan.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/icons/pause.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/icons/pencil.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/icons/play.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/icons/plus.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/icons/radio.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/icons/reset.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/icons/save.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/icons/stop.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/icons/tilemap.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/icons/tileset.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/icons/unchecked.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/icons/warning.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/icons/zoomin.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/assets/icons/zoomout.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/setup.cfg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/configs/themes.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/constants.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/editor.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/event_map.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/layers.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/main.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/node_manager.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/nodes.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/__init__.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/character_collision/__init__.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/character_collision/editor.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/character_collision/models.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/character_collision/protocols.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/character_collision/shape_editor.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/character_collision/standalone.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/object_tileset_collision/__init__.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/object_tileset_collision/editor.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/object_tileset_collision/models.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/object_tileset_collision/protocols.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/object_tileset_collision/standalone.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/sprite_animation/__init__.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/sprite_animation/__main__.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/sprite_animation/clipboard_util.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/sprite_animation/editor.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/sprite_animation/frame_picker.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/sprite_animation/models.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/sprite_animation/preview.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/sprite_animation/protocols.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/sprite_animation/runtime_load.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/sprite_animation/standalone.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/sprite_animation/timeline.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/sprite_animation/validation.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/sprite_editor/__init__.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/sprite_editor/standalone.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/tileset_collision/__init__.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/tileset_collision/models.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/tileset_collision/protocols.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/tileset_collision/standalone.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/standalone_automap.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/standalone_error_console.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/standalone_filemanager.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/standalone_image_viewer.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/__init__.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/__main__.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/__init__.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/ToolMove.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/arrow-down.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/check.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/checked.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/close.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/duplicate.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/error.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/file.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/filedead.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/fit.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/folder.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/grid.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/image.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/info.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/load.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/loop.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/pan.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/pause.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/pencil.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/play.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/plus.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/radio.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/reset.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/save.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/stop.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/tilemap.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/tileset.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/unchecked.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/warning.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/zoomin.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/zoomout.svg +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/cli.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/main.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor/settings.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor.egg-info/dependency_links.txt +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor.egg-info/entry_points.txt +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor.egg-info/requires.txt +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/tilemap_editor.egg-info/top_level.txt +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/ttypes/__init__.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/ttypes/tilemap.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/utils/__init__.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/utils/editor_preference.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/utils/error_handler.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/utils/font_manager.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/utils/history.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/utils/icon_manager.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/utils/icons_cache.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/utils/log_capture.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/utils/project_paths.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/utils/serialization.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/utils/standalone.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/utils/validation.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/__init__.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/automap_models.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/autotile_template.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/autotiler.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/input.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/layer_selector.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/map_properties.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/mapsetup.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/particle_presets.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/particle_system.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/regex_automap_designer.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/tile_grid.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/tile_selector.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/ui/collision_layer_mask.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/ui/collision_layer_sidebar.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/ui/confirm_dialog.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/ui/draw_utils.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/ui/fileinput.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/ui/layer_type_dialog.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/ui/menubar.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/ui/mode_indicator.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/ui/node_editor.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/ui/node_selector.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/ui/notification.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/ui/particle_config_dialog.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/ui/property_editor.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/ui/region_selector.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/ui/status_bar.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/ui/theme.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/ui/tileset_type_dialog.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/ui/toolbar.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/widgets/ui/tooltip.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/tests/test_animation_library_grid_offset.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/tests/test_collision_layer_mask.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/tests/test_collision_layer_sidebar.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/tests/test_editor_pan_mode.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/tests/test_project_paths.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/tests/test_render_scale.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/tests/test_sprite_animation_editor_grid.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/tests/test_tile_grid_selection.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/tests/test_tile_selector_pick.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/tests/test_tilemap_save.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/tests/test_timeline_invalidate_cache.py +0 -0
- {tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/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 = "3.3.
|
|
7
|
+
version = "3.3.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"
|
|
@@ -149,6 +149,24 @@ class SpriteEditor:
|
|
|
149
149
|
if self.grid.redo():
|
|
150
150
|
self.grid.selected_indices.clear()
|
|
151
151
|
return True
|
|
152
|
+
elif event.key == pygame.K_c:
|
|
153
|
+
if self.grid.has_selection():
|
|
154
|
+
self._clipboard = self.grid.copy_selected()
|
|
155
|
+
self._paste_mode = False
|
|
156
|
+
self.grid.paste_preview_idx = -1
|
|
157
|
+
return True
|
|
158
|
+
elif event.key == pygame.K_x:
|
|
159
|
+
if self.grid.has_selection():
|
|
160
|
+
self._clipboard = self.grid.cut_selected()
|
|
161
|
+
self._paste_mode = False
|
|
162
|
+
self.grid.paste_preview_idx = -1
|
|
163
|
+
return True
|
|
164
|
+
elif event.key == pygame.K_v:
|
|
165
|
+
if self._clipboard:
|
|
166
|
+
self._paste_mode = not self._paste_mode
|
|
167
|
+
if not self._paste_mode:
|
|
168
|
+
self.grid.paste_preview_idx = -1
|
|
169
|
+
return True
|
|
152
170
|
|
|
153
171
|
# Toolbar clicks
|
|
154
172
|
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
|
|
@@ -156,19 +174,20 @@ class SpriteEditor:
|
|
|
156
174
|
if r.collidepoint(mouse):
|
|
157
175
|
return self._handle_tool(lbl)
|
|
158
176
|
|
|
159
|
-
# Paste mode:
|
|
177
|
+
# Paste mode: click places (or exits if outside grid)
|
|
160
178
|
if self._paste_mode and event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
|
|
161
179
|
idx = self.grid.index_at_pos(mouse)
|
|
162
180
|
if idx >= 0 and self.grid.rect.collidepoint(mouse):
|
|
163
181
|
self.grid.paste_at(idx, self._clipboard)
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
182
|
+
self._paste_mode = False
|
|
183
|
+
self.grid.paste_preview_idx = -1
|
|
184
|
+
return True
|
|
167
185
|
|
|
168
|
-
# Paste preview on hover
|
|
186
|
+
# Paste preview on hover (consume event to prevent grid overlap)
|
|
169
187
|
if self._paste_mode and event.type == pygame.MOUSEMOTION:
|
|
170
188
|
idx = self.grid.index_at_pos(mouse)
|
|
171
189
|
self.grid.paste_preview_idx = idx if idx >= 0 else -1
|
|
190
|
+
return True
|
|
172
191
|
|
|
173
192
|
return self.grid.handle_event(event)
|
|
174
193
|
|
{tilemap_editor-3.3.3 → tilemap_editor-3.3.4}/src/plugins/tileset_collision/collision_painter.py
RENAMED
|
@@ -117,7 +117,8 @@ class CollisionPainter:
|
|
|
117
117
|
self.snap_to_grid = False
|
|
118
118
|
self.grid_size = 8 # pixels
|
|
119
119
|
self.show_grid = True
|
|
120
|
-
self.
|
|
120
|
+
self.show_angle_hints = False # show angle arcs at vertices
|
|
121
|
+
self.edge_draw_mode = False # Ctrl+E - straight line drawing
|
|
121
122
|
self._edge_start: Optional[Tuple[int, int]] = None # Start point for edge draw
|
|
122
123
|
self._shift_held = False
|
|
123
124
|
|
|
@@ -270,7 +271,66 @@ class CollisionPainter:
|
|
|
270
271
|
def get_one_way_flags(self) -> List[bool]:
|
|
271
272
|
"""Get one-way collision flags for all polygons"""
|
|
272
273
|
return list(self.polygon_one_way)
|
|
274
|
+
|
|
275
|
+
def toggle_one_way(self) -> None:
|
|
276
|
+
"""Toggle one-way flag on the selected polygon"""
|
|
277
|
+
idx = self.selected_polygon_idx
|
|
278
|
+
if idx is not None and 0 <= idx < len(self.polygon_one_way):
|
|
279
|
+
self.polygon_one_way[idx] = not self.polygon_one_way[idx]
|
|
280
|
+
if self.on_polygon_modified:
|
|
281
|
+
self.on_polygon_modified(idx)
|
|
273
282
|
|
|
283
|
+
def _get_interior_angle(
|
|
284
|
+
self, polygon: List[Tuple[float, float]], idx: int
|
|
285
|
+
) -> float:
|
|
286
|
+
"""Compute interior angle (degrees) at polygon vertex idx."""
|
|
287
|
+
n = len(polygon)
|
|
288
|
+
if n < 3:
|
|
289
|
+
return 0.0
|
|
290
|
+
prev = polygon[(idx - 1) % n]
|
|
291
|
+
curr = polygon[idx]
|
|
292
|
+
nxt = polygon[(idx + 1) % n]
|
|
293
|
+
v1 = (prev[0] - curr[0], prev[1] - curr[1])
|
|
294
|
+
v2 = (nxt[0] - curr[0], nxt[1] - curr[1])
|
|
295
|
+
dot = v1[0] * v2[0] + v1[1] * v2[1]
|
|
296
|
+
mag1 = math.hypot(v1[0], v1[1])
|
|
297
|
+
mag2 = math.hypot(v2[0], v2[1])
|
|
298
|
+
if mag1 < 0.001 or mag2 < 0.001:
|
|
299
|
+
return 0.0
|
|
300
|
+
cos_a = max(-1.0, min(1.0, dot / (mag1 * mag2)))
|
|
301
|
+
return math.degrees(math.acos(cos_a))
|
|
302
|
+
|
|
303
|
+
def _draw_angle_hint(
|
|
304
|
+
self,
|
|
305
|
+
screen: pygame.Surface,
|
|
306
|
+
screen_pos: Tuple[int, int],
|
|
307
|
+
angle_deg: float,
|
|
308
|
+
) -> None:
|
|
309
|
+
"""Draw a small arc + angle text at a vertex."""
|
|
310
|
+
if angle_deg <= 0.5 or angle_deg >= 179.5:
|
|
311
|
+
return
|
|
312
|
+
arc_radius = 14
|
|
313
|
+
segments = max(4, int(angle_deg / 10))
|
|
314
|
+
angle_rad = math.radians(angle_deg)
|
|
315
|
+
pts = [screen_pos]
|
|
316
|
+
for i in range(segments + 1):
|
|
317
|
+
t = -angle_rad * i / segments
|
|
318
|
+
pts.append((
|
|
319
|
+
screen_pos[0] + arc_radius * math.cos(t),
|
|
320
|
+
screen_pos[1] + arc_radius * math.sin(t),
|
|
321
|
+
))
|
|
322
|
+
if len(pts) >= 3:
|
|
323
|
+
wedge = pygame.Surface(self.rect.size, pygame.SRCALPHA)
|
|
324
|
+
pygame.draw.polygon(wedge, (*_COLORS["polygon_fill"], 60), pts)
|
|
325
|
+
screen.blit(wedge, self.rect.topleft)
|
|
326
|
+
label = self._font_sm.render(f"{angle_deg:.0f}°", True, _COLORS["text"])
|
|
327
|
+
label_x = screen_pos[0] + 10
|
|
328
|
+
label_y = screen_pos[1] - label.get_height() - 4
|
|
329
|
+
bg = pygame.Surface((label.get_width() + 4, label.get_height() + 2), pygame.SRCALPHA)
|
|
330
|
+
bg.fill((0, 0, 0, 180))
|
|
331
|
+
screen.blit(bg, (label_x - 2, label_y - 1))
|
|
332
|
+
screen.blit(label, (label_x, label_y))
|
|
333
|
+
|
|
274
334
|
def clear_all(self) -> None:
|
|
275
335
|
"""Clear all polygons"""
|
|
276
336
|
self.polygons = []
|
|
@@ -488,23 +548,26 @@ class CollisionPainter:
|
|
|
488
548
|
|
|
489
549
|
# Keyboard shortcuts
|
|
490
550
|
if event.type == pygame.KEYDOWN:
|
|
551
|
+
mods = pygame.key.get_mods()
|
|
552
|
+
ctrl = mods & (pygame.KMOD_LCTRL | pygame.KMOD_LMETA)
|
|
553
|
+
|
|
491
554
|
# Track Shift state for edge draw
|
|
492
555
|
if event.key in (pygame.K_LSHIFT, pygame.K_RSHIFT):
|
|
493
556
|
self._shift_held = True
|
|
494
|
-
|
|
495
|
-
# E - toggle edge draw mode
|
|
496
|
-
elif event.key == pygame.K_e:
|
|
557
|
+
|
|
558
|
+
# Ctrl+E - toggle edge draw mode
|
|
559
|
+
elif ctrl and event.key == pygame.K_e:
|
|
497
560
|
self.edge_draw_mode = not self.edge_draw_mode
|
|
498
561
|
if not self.edge_draw_mode:
|
|
499
562
|
self._edge_start = None
|
|
500
563
|
return True
|
|
501
|
-
|
|
564
|
+
|
|
502
565
|
# Enter - complete polygon
|
|
503
566
|
elif event.key in (pygame.K_RETURN, pygame.K_KP_ENTER):
|
|
504
567
|
if len(self.current_polygon) >= 3:
|
|
505
568
|
self._complete_polygon()
|
|
506
569
|
return True
|
|
507
|
-
|
|
570
|
+
|
|
508
571
|
# Escape - cancel current polygon or deselect or close help
|
|
509
572
|
elif event.key == pygame.K_ESCAPE:
|
|
510
573
|
if self.show_help:
|
|
@@ -517,44 +580,45 @@ class CollisionPainter:
|
|
|
517
580
|
self.selected_polygon_idx = None
|
|
518
581
|
self.selected_vertex_idx = None
|
|
519
582
|
return True
|
|
520
|
-
|
|
521
|
-
# Delete - remove selected polygon
|
|
583
|
+
|
|
584
|
+
# Delete/Backspace - remove selected polygon or last vertex
|
|
522
585
|
elif event.key in (pygame.K_DELETE, pygame.K_BACKSPACE):
|
|
523
586
|
if self.selected_polygon_idx is not None:
|
|
524
587
|
self._delete_polygon(self.selected_polygon_idx)
|
|
525
588
|
return True
|
|
526
589
|
elif self.current_polygon:
|
|
527
|
-
# Remove last vertex
|
|
528
590
|
self.current_polygon.pop()
|
|
529
591
|
return True
|
|
530
|
-
|
|
531
|
-
# G - toggle grid
|
|
532
|
-
elif event.key == pygame.K_g:
|
|
592
|
+
|
|
593
|
+
# Ctrl+G - toggle grid
|
|
594
|
+
elif ctrl and event.key == pygame.K_g:
|
|
533
595
|
self.show_grid = not self.show_grid
|
|
534
596
|
return True
|
|
535
|
-
|
|
536
|
-
# S - toggle snap (
|
|
537
|
-
elif event.key == pygame.K_s:
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
# O - toggle one-way collision for selected polygon
|
|
545
|
-
elif event.key == pygame.K_o:
|
|
597
|
+
|
|
598
|
+
# Ctrl+Shift+S - toggle snap (Ctrl+S reserved for save)
|
|
599
|
+
elif ctrl and event.key == pygame.K_s and (mods & pygame.KMOD_SHIFT):
|
|
600
|
+
self.snap_to_grid = not self.snap_to_grid
|
|
601
|
+
return True
|
|
602
|
+
|
|
603
|
+
# Ctrl+O - toggle one-way collision for selected polygon
|
|
604
|
+
elif ctrl and event.key == pygame.K_o:
|
|
546
605
|
if self.selected_polygon_idx is not None:
|
|
547
606
|
idx = self.selected_polygon_idx
|
|
548
607
|
self.polygon_one_way[idx] = not self.polygon_one_way[idx]
|
|
549
608
|
if self.on_polygon_modified:
|
|
550
609
|
self.on_polygon_modified(idx)
|
|
551
610
|
return True
|
|
552
|
-
|
|
553
|
-
# R - reset view
|
|
554
|
-
elif event.key == pygame.K_r:
|
|
611
|
+
|
|
612
|
+
# Ctrl+R - reset view
|
|
613
|
+
elif ctrl and event.key == pygame.K_r:
|
|
555
614
|
self._center_view()
|
|
556
615
|
self.zoom = 2.0
|
|
557
616
|
return True
|
|
617
|
+
|
|
618
|
+
# Ctrl+A - toggle angle hints
|
|
619
|
+
elif ctrl and event.key == pygame.K_a:
|
|
620
|
+
self.show_angle_hints = not self.show_angle_hints
|
|
621
|
+
return True
|
|
558
622
|
|
|
559
623
|
if event.type == pygame.KEYUP:
|
|
560
624
|
if event.key in (pygame.K_LSHIFT, pygame.K_RSHIFT):
|
|
@@ -744,20 +808,21 @@ class CollisionPainter:
|
|
|
744
808
|
("Right-click / Enter", "Complete polygon"),
|
|
745
809
|
("Escape", "Cancel current polygon"),
|
|
746
810
|
("Shift (edge mode)", "Constrain to axis"),
|
|
747
|
-
("E", "Toggle edge draw mode"),
|
|
811
|
+
("Ctrl+E", "Toggle edge draw mode"),
|
|
748
812
|
]),
|
|
749
813
|
("SELECTION", [
|
|
750
814
|
("Left-click polygon", "Select polygon"),
|
|
751
815
|
("Left-click vertex", "Select & drag vertex"),
|
|
752
816
|
("Delete / Backspace", "Remove selected polygon"),
|
|
753
|
-
("O", "Toggle one-way collision"),
|
|
817
|
+
("Ctrl+O", "Toggle one-way collision"),
|
|
754
818
|
]),
|
|
755
819
|
("VIEW", [
|
|
756
820
|
("Mouse wheel", "Zoom in/out"),
|
|
757
821
|
("Middle mouse / Space+LMB", "Pan view"),
|
|
758
|
-
("G", "Toggle grid"),
|
|
759
|
-
("S", "Toggle snap to grid"),
|
|
760
|
-
("R", "Reset view"),
|
|
822
|
+
("Ctrl+G", "Toggle grid"),
|
|
823
|
+
("Ctrl+Shift+S", "Toggle snap to grid"),
|
|
824
|
+
("Ctrl+R", "Reset view"),
|
|
825
|
+
("Ctrl+A", "Toggle angle hints"),
|
|
761
826
|
]),
|
|
762
827
|
("MISC", [
|
|
763
828
|
("Info button (top-right)", "Toggle this help panel"),
|
|
@@ -905,6 +970,24 @@ class CollisionPainter:
|
|
|
905
970
|
radius = VERTEX_HOVER_RADIUS if is_hovered_vertex else VERTEX_RADIUS
|
|
906
971
|
pygame.draw.circle(screen, color, (px, py), radius)
|
|
907
972
|
pygame.draw.circle(screen, (0, 0, 0), (px, py), radius, 1)
|
|
973
|
+
|
|
974
|
+
# One-way badge on selected polygon
|
|
975
|
+
if selected and one_way and len(polygon) >= 3:
|
|
976
|
+
cx = int(sum(p[0] for p in screen_points) / len(screen_points))
|
|
977
|
+
cy = int(sum(p[1] for p in screen_points) / len(screen_points))
|
|
978
|
+
badge = self._font_sm.render("ONE-WAY", True, (255, 255, 255))
|
|
979
|
+
bx = cx - badge.get_width() // 2
|
|
980
|
+
by = cy - badge.get_height() // 2
|
|
981
|
+
bg = pygame.Surface((badge.get_width() + 8, badge.get_height() + 4), pygame.SRCALPHA)
|
|
982
|
+
bg.fill((_COLORS["one_way"][0], _COLORS["one_way"][1], _COLORS["one_way"][2], 200))
|
|
983
|
+
screen.blit(bg, (bx - 4, by - 2))
|
|
984
|
+
screen.blit(badge, (bx, by))
|
|
985
|
+
|
|
986
|
+
# Angle hints
|
|
987
|
+
if self.show_angle_hints:
|
|
988
|
+
for i, (px, py) in enumerate(screen_points):
|
|
989
|
+
angle = self._get_interior_angle(polygon, i)
|
|
990
|
+
self._draw_angle_hint(screen, (px, py), angle)
|
|
908
991
|
|
|
909
992
|
def _draw_current_polygon(self, screen: pygame.Surface) -> None:
|
|
910
993
|
"""Draw the polygon currently being drawn"""
|
|
@@ -934,6 +1017,25 @@ class CollisionPainter:
|
|
|
934
1017
|
radius = VERTEX_HOVER_RADIUS if is_first else VERTEX_RADIUS
|
|
935
1018
|
pygame.draw.circle(screen, color, (px, py), radius)
|
|
936
1019
|
pygame.draw.circle(screen, (0, 0, 0), (px, py), radius, 1)
|
|
1020
|
+
|
|
1021
|
+
# Angle hints for vertices with both neighbors
|
|
1022
|
+
if self.show_angle_hints and len(self.current_polygon) >= 3:
|
|
1023
|
+
for i, (px, py) in enumerate(screen_points):
|
|
1024
|
+
if i == 0 or i == len(screen_points) - 1:
|
|
1025
|
+
continue
|
|
1026
|
+
poly = self.current_polygon
|
|
1027
|
+
prev = poly[i - 1]
|
|
1028
|
+
curr = poly[i]
|
|
1029
|
+
nxt = poly[i + 1]
|
|
1030
|
+
v1 = (prev[0] - curr[0], prev[1] - curr[1])
|
|
1031
|
+
v2 = (nxt[0] - curr[0], nxt[1] - curr[1])
|
|
1032
|
+
dot = v1[0] * v2[0] + v1[1] * v2[1]
|
|
1033
|
+
mag1 = math.hypot(v1[0], v1[1])
|
|
1034
|
+
mag2 = math.hypot(v2[0], v2[1])
|
|
1035
|
+
if mag1 > 0.001 and mag2 > 0.001:
|
|
1036
|
+
cos_a = max(-1.0, min(1.0, dot / (mag1 * mag2)))
|
|
1037
|
+
angle = math.degrees(math.acos(cos_a))
|
|
1038
|
+
self._draw_angle_hint(screen, (px, py), angle)
|
|
937
1039
|
|
|
938
1040
|
def _draw_status(self, screen: pygame.Surface) -> None:
|
|
939
1041
|
"""Draw status text"""
|
|
@@ -949,12 +1051,13 @@ class CollisionPainter:
|
|
|
949
1051
|
one_way = self.polygon_one_way[self.selected_polygon_idx]
|
|
950
1052
|
one_way_str = " (ONE-WAY)" if one_way else ""
|
|
951
1053
|
lines.append(f"Selected polygon {self.selected_polygon_idx}{one_way_str}")
|
|
952
|
-
lines.append("Delete to remove, O to toggle one-way")
|
|
1054
|
+
lines.append("Delete to remove, Ctrl+O to toggle one-way")
|
|
953
1055
|
else:
|
|
954
1056
|
lines.append("Click to add vertices, right-click to complete")
|
|
955
1057
|
|
|
956
|
-
|
|
957
|
-
lines.append(f"
|
|
1058
|
+
snap_str = f"Snap: {'ON' if self.snap_to_grid else 'OFF'} (Ctrl+Shift+S)"
|
|
1059
|
+
lines.append(f"Zoom: {self.zoom:.1f}x | Grid: {'ON' if self.show_grid else 'OFF'} (Ctrl+G) | {snap_str}")
|
|
1060
|
+
lines.append(f"Polygons: {len(self.polygons)} | Ctrl+R: reset | Ctrl+A: angle hints {'ON' if self.show_angle_hints else 'OFF'}")
|
|
958
1061
|
|
|
959
1062
|
y = self.rect.y + 5
|
|
960
1063
|
for line in lines:
|
|
@@ -30,6 +30,17 @@ from utils.icon_manager import icon_manager
|
|
|
30
30
|
from utils.error_handler import error_handler, error_context
|
|
31
31
|
from widgets.ui.theme import COLORS, FONTS
|
|
32
32
|
from widgets.ui.draw_utils import draw_panel, draw_button
|
|
33
|
+
from widgets.ui.checkbox import Checkbox
|
|
34
|
+
|
|
35
|
+
# Widget panel layout constants
|
|
36
|
+
WP_PADDING_X = 12
|
|
37
|
+
WP_PADDING_Y = 38
|
|
38
|
+
WP_SECTION_HEIGHT = 28
|
|
39
|
+
WP_CHECKBOX_ROW = 30
|
|
40
|
+
WP_GRID_ROW = 36
|
|
41
|
+
WP_GRID_BTN = 28
|
|
42
|
+
WP_GRID_GAP = 68
|
|
43
|
+
WP_TITLE_Y = 10
|
|
33
44
|
|
|
34
45
|
|
|
35
46
|
class TilesetCollisionEditor:
|
|
@@ -75,6 +86,9 @@ class TilesetCollisionEditor:
|
|
|
75
86
|
# Auto-tile propagation: group_id -> [variant_ids]
|
|
76
87
|
self._propagation_groups: Dict[str, List[int]] = {}
|
|
77
88
|
|
|
89
|
+
# Tiles the user explicitly cleared — propagation won't re-add to these
|
|
90
|
+
self._user_cleared_tiles: Set[int] = set()
|
|
91
|
+
|
|
78
92
|
# Collision library
|
|
79
93
|
self.library = TilesetCollisionLibrary(
|
|
80
94
|
tileset_name=self._tileset_name,
|
|
@@ -91,6 +105,7 @@ class TilesetCollisionEditor:
|
|
|
91
105
|
self.toolbar_height = 40
|
|
92
106
|
self.tileset_selector_height = 250 # Resizable
|
|
93
107
|
self.painted_tiles_width = 200
|
|
108
|
+
self.widget_panel_width = 200
|
|
94
109
|
self._resizing_selector = False
|
|
95
110
|
self._resize_start_y = 0
|
|
96
111
|
self._resize_start_height = 0
|
|
@@ -128,6 +143,9 @@ class TilesetCollisionEditor:
|
|
|
128
143
|
# Toolbar buttons
|
|
129
144
|
self._setup_toolbar_buttons()
|
|
130
145
|
|
|
146
|
+
# Widget panel buttons
|
|
147
|
+
self._setup_widget_buttons()
|
|
148
|
+
|
|
131
149
|
# Load collision data for first tile
|
|
132
150
|
self._load_tile_collision_for_selection()
|
|
133
151
|
|
|
@@ -151,6 +169,40 @@ class TilesetCollisionEditor:
|
|
|
151
169
|
"hovered": False,
|
|
152
170
|
})
|
|
153
171
|
|
|
172
|
+
def _setup_widget_buttons(self) -> None:
|
|
173
|
+
"""Setup widget panel buttons"""
|
|
174
|
+
self._chk_one_way = Checkbox(
|
|
175
|
+
Rect(0, 0, 0, 0), "One-Way",
|
|
176
|
+
disabled=True,
|
|
177
|
+
on_changed=lambda _: self.painter.toggle_one_way(),
|
|
178
|
+
)
|
|
179
|
+
self._chk_angle = Checkbox(
|
|
180
|
+
Rect(0, 0, 0, 0), "Angle Hints",
|
|
181
|
+
on_changed=lambda v: setattr(self.painter, "show_angle_hints", v),
|
|
182
|
+
)
|
|
183
|
+
self._chk_grid = Checkbox(
|
|
184
|
+
Rect(0, 0, 0, 0), "Grid",
|
|
185
|
+
checked=True,
|
|
186
|
+
on_changed=lambda v: setattr(self.painter, "show_grid", v),
|
|
187
|
+
)
|
|
188
|
+
self._chk_snap = Checkbox(
|
|
189
|
+
Rect(0, 0, 0, 0), "Snap to Grid",
|
|
190
|
+
on_changed=lambda v: setattr(self.painter, "snap_to_grid", v),
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
self._widget_items: List[tuple] = [
|
|
194
|
+
("section", "POLYGON"),
|
|
195
|
+
("checkbox", self._chk_one_way),
|
|
196
|
+
("checkbox", self._chk_angle),
|
|
197
|
+
("section", "DISPLAY"),
|
|
198
|
+
("checkbox", self._chk_grid),
|
|
199
|
+
("section", "GRID SNAP"),
|
|
200
|
+
("grid_dec", None),
|
|
201
|
+
("grid_val", None),
|
|
202
|
+
("grid_inc", None),
|
|
203
|
+
("checkbox", self._chk_snap),
|
|
204
|
+
]
|
|
205
|
+
|
|
154
206
|
def _save_collision(self) -> None:
|
|
155
207
|
"""Save collision via toolbar button"""
|
|
156
208
|
collision_dir = self._get_collision_dir()
|
|
@@ -200,6 +252,64 @@ class TilesetCollisionEditor:
|
|
|
200
252
|
shape_count = len(self._clipboard_polygons)
|
|
201
253
|
self._show_toast(f"Pasted {shape_count} shape{'s' if shape_count != 1 else ''} to {tile_count} tile{'s' if tile_count != 1 else ''}")
|
|
202
254
|
|
|
255
|
+
def _layout_widget_panel(self) -> None:
|
|
256
|
+
"""Pre-compute clickable widget rects (checkbox + grid buttons)"""
|
|
257
|
+
panel = self.widget_panel_rect
|
|
258
|
+
px = panel.x + WP_PADDING_X
|
|
259
|
+
pw = panel.w - WP_PADDING_X * 2
|
|
260
|
+
y = panel.y + WP_PADDING_Y
|
|
261
|
+
for kind, data in self._widget_items:
|
|
262
|
+
if kind == "section":
|
|
263
|
+
y += WP_SECTION_HEIGHT
|
|
264
|
+
elif kind == "checkbox":
|
|
265
|
+
data.rect = Rect(px, y, pw, WP_CHECKBOX_ROW)
|
|
266
|
+
y += WP_CHECKBOX_ROW
|
|
267
|
+
elif kind == "grid_dec":
|
|
268
|
+
self._grid_dec_rect = Rect(px, y, WP_GRID_BTN, WP_GRID_BTN)
|
|
269
|
+
elif kind == "grid_val":
|
|
270
|
+
self._grid_val_rect = Rect(px, y, 0, WP_GRID_BTN)
|
|
271
|
+
elif kind == "grid_inc":
|
|
272
|
+
self._grid_inc_rect = Rect(px + WP_GRID_GAP, y, WP_GRID_BTN, WP_GRID_BTN)
|
|
273
|
+
y += WP_GRID_ROW
|
|
274
|
+
|
|
275
|
+
def _sync_widget_state(self) -> None:
|
|
276
|
+
"""Sync checkbox states from painter attributes before draw"""
|
|
277
|
+
p = self.painter
|
|
278
|
+
self._chk_grid.checked = p.show_grid
|
|
279
|
+
self._chk_snap.checked = p.snap_to_grid
|
|
280
|
+
self._chk_angle.checked = p.show_angle_hints
|
|
281
|
+
sel = p.selected_polygon_idx
|
|
282
|
+
if sel is not None and 0 <= sel < len(p.polygon_one_way):
|
|
283
|
+
self._chk_one_way.checked = p.polygon_one_way[sel]
|
|
284
|
+
self._chk_one_way.disabled = False
|
|
285
|
+
else:
|
|
286
|
+
self._chk_one_way.checked = False
|
|
287
|
+
self._chk_one_way.disabled = True
|
|
288
|
+
|
|
289
|
+
def _handle_widget_button_clicks(self, events: List[pygame.event.Event]) -> None:
|
|
290
|
+
"""Handle widget panel button clicks"""
|
|
291
|
+
self._sync_widget_state()
|
|
292
|
+
self._layout_widget_panel()
|
|
293
|
+
painter = self.painter
|
|
294
|
+
mouse = pygame.mouse.get_pos()
|
|
295
|
+
|
|
296
|
+
# Pass click events to checkboxes
|
|
297
|
+
for event in events:
|
|
298
|
+
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
|
|
299
|
+
pos = event.pos
|
|
300
|
+
if self._chk_one_way.handle_event(event):
|
|
301
|
+
continue
|
|
302
|
+
if self._chk_angle.handle_event(event):
|
|
303
|
+
continue
|
|
304
|
+
if self._chk_grid.handle_event(event):
|
|
305
|
+
continue
|
|
306
|
+
if self._chk_snap.handle_event(event):
|
|
307
|
+
continue
|
|
308
|
+
if self._grid_dec_rect.collidepoint(pos):
|
|
309
|
+
painter.grid_size = max(1, painter.grid_size - 1)
|
|
310
|
+
elif self._grid_inc_rect.collidepoint(pos):
|
|
311
|
+
painter.grid_size = min(64, painter.grid_size + 1)
|
|
312
|
+
|
|
203
313
|
def _position_toolbar_buttons(self) -> None:
|
|
204
314
|
"""Position toolbar buttons in the toolbar area"""
|
|
205
315
|
if not hasattr(self, '_toolbar_buttons'):
|
|
@@ -226,6 +336,68 @@ class TilesetCollisionEditor:
|
|
|
226
336
|
if button["rect"].collidepoint(mouse_pos):
|
|
227
337
|
button["action"]()
|
|
228
338
|
|
|
339
|
+
def _draw_widget_panel(self, surface: Surface) -> None:
|
|
340
|
+
"""Draw the widget panel on the right side"""
|
|
341
|
+
self._sync_widget_state()
|
|
342
|
+
panel = self.widget_panel_rect
|
|
343
|
+
pygame.draw.rect(surface, COLORS.panel_alt, panel)
|
|
344
|
+
pygame.draw.rect(surface, COLORS.border_soft, panel, 1)
|
|
345
|
+
|
|
346
|
+
px = panel.x + WP_PADDING_X
|
|
347
|
+
pw = panel.w - WP_PADDING_X * 2
|
|
348
|
+
y = panel.y + WP_PADDING_Y
|
|
349
|
+
|
|
350
|
+
title = self._font.render("Tools", True, COLORS.text)
|
|
351
|
+
surface.blit(title, (px, panel.y + WP_TITLE_Y))
|
|
352
|
+
|
|
353
|
+
painter = self.painter
|
|
354
|
+
mouse = pygame.mouse.get_pos()
|
|
355
|
+
|
|
356
|
+
for kind, data in self._widget_items:
|
|
357
|
+
if kind == "section":
|
|
358
|
+
lbl = self._font_sm.render(data, True, (140, 140, 160))
|
|
359
|
+
surface.blit(lbl, (px, y + 4))
|
|
360
|
+
sep_y = y + lbl.get_height() + 8
|
|
361
|
+
pygame.draw.line(surface, (50, 50, 55), (px, sep_y), (px + pw, sep_y))
|
|
362
|
+
y += WP_SECTION_HEIGHT
|
|
363
|
+
|
|
364
|
+
elif kind == "checkbox":
|
|
365
|
+
data.rect = Rect(px, y, pw, WP_CHECKBOX_ROW)
|
|
366
|
+
data.draw(surface)
|
|
367
|
+
y += WP_CHECKBOX_ROW
|
|
368
|
+
|
|
369
|
+
elif kind == "grid_dec":
|
|
370
|
+
r = Rect(px, y, WP_GRID_BTN, WP_GRID_BTN)
|
|
371
|
+
self._grid_dec_rect = r
|
|
372
|
+
hovered = r.collidepoint(mouse)
|
|
373
|
+
bg = (55, 55, 62) if hovered else (45, 45, 50)
|
|
374
|
+
pygame.draw.rect(surface, bg, r, border_radius=4)
|
|
375
|
+
pygame.draw.rect(surface, (65, 65, 72), r, 1, border_radius=4)
|
|
376
|
+
lbl = self._font_sm.render("-", True, COLORS.text)
|
|
377
|
+
tx = r.centerx - lbl.get_width() // 2
|
|
378
|
+
ty = r.centery - lbl.get_height() // 2
|
|
379
|
+
surface.blit(lbl, (tx, ty))
|
|
380
|
+
|
|
381
|
+
elif kind == "grid_val":
|
|
382
|
+
self._grid_val_rect = Rect(px, y, 0, WP_GRID_BTN)
|
|
383
|
+
val_surf = self._font_sm.render(str(painter.grid_size), True, COLORS.text)
|
|
384
|
+
vx = px + WP_GRID_BTN + 8
|
|
385
|
+
vy = y + (WP_GRID_BTN - val_surf.get_height()) // 2
|
|
386
|
+
surface.blit(val_surf, (vx, vy))
|
|
387
|
+
|
|
388
|
+
elif kind == "grid_inc":
|
|
389
|
+
r = Rect(px + WP_GRID_GAP, y, WP_GRID_BTN, WP_GRID_BTN)
|
|
390
|
+
self._grid_inc_rect = r
|
|
391
|
+
hovered = r.collidepoint(mouse)
|
|
392
|
+
bg = (55, 55, 62) if hovered else (45, 45, 50)
|
|
393
|
+
pygame.draw.rect(surface, bg, r, border_radius=4)
|
|
394
|
+
pygame.draw.rect(surface, (65, 65, 72), r, 1, border_radius=4)
|
|
395
|
+
lbl = self._font_sm.render("+", True, COLORS.text)
|
|
396
|
+
tx = r.centerx - lbl.get_width() // 2
|
|
397
|
+
ty = r.centery - lbl.get_height() // 2
|
|
398
|
+
surface.blit(lbl, (tx, ty))
|
|
399
|
+
y += WP_GRID_ROW
|
|
400
|
+
|
|
229
401
|
def _draw_toolbar_buttons(self, surface: Surface) -> None:
|
|
230
402
|
"""Draw toolbar buttons"""
|
|
231
403
|
for button in self._toolbar_buttons:
|
|
@@ -272,7 +444,14 @@ class TilesetCollisionEditor:
|
|
|
272
444
|
self.painter_rect = Rect(
|
|
273
445
|
self.rect.x + self.painted_tiles_width,
|
|
274
446
|
middle_y,
|
|
275
|
-
self.rect.w - self.painted_tiles_width,
|
|
447
|
+
self.rect.w - self.painted_tiles_width - self.widget_panel_width,
|
|
448
|
+
middle_h
|
|
449
|
+
)
|
|
450
|
+
|
|
451
|
+
self.widget_panel_rect = Rect(
|
|
452
|
+
self.rect.right - self.widget_panel_width,
|
|
453
|
+
middle_y,
|
|
454
|
+
self.widget_panel_width,
|
|
276
455
|
middle_h
|
|
277
456
|
)
|
|
278
457
|
|
|
@@ -342,7 +521,11 @@ class TilesetCollisionEditor:
|
|
|
342
521
|
del self.library.tiles[tile_id]
|
|
343
522
|
if self.consumer:
|
|
344
523
|
self.consumer.on_collision_deleted(tile_id)
|
|
524
|
+
# Mark as user-cleared so propagation doesn't re-add it
|
|
525
|
+
self._user_cleared_tiles.add(tile_id)
|
|
345
526
|
else:
|
|
527
|
+
# User painted collision — remove from cleared set
|
|
528
|
+
self._user_cleared_tiles.discard(tile_id)
|
|
346
529
|
# Create collision shapes
|
|
347
530
|
shapes = [
|
|
348
531
|
CollisionPolygon(vertices=poly, one_way=one_way)
|
|
@@ -471,7 +654,10 @@ class TilesetCollisionEditor:
|
|
|
471
654
|
continue
|
|
472
655
|
|
|
473
656
|
# Propagate to all variants in the group that lack collision
|
|
657
|
+
# Skip tiles the user explicitly cleared
|
|
474
658
|
for vid in variant_ids:
|
|
659
|
+
if vid in self._user_cleared_tiles:
|
|
660
|
+
continue
|
|
475
661
|
existing = self.library.tiles.get(vid)
|
|
476
662
|
if existing is None:
|
|
477
663
|
self.library.tiles[vid] = TileCollisionData(
|
|
@@ -696,6 +882,9 @@ class TilesetCollisionEditor:
|
|
|
696
882
|
# Draw tileset selector
|
|
697
883
|
self._draw_tileset_selector(screen)
|
|
698
884
|
|
|
885
|
+
# Draw widget panel
|
|
886
|
+
self._draw_widget_panel(screen)
|
|
887
|
+
|
|
699
888
|
# Draw toast overlay
|
|
700
889
|
self._draw_toast(screen)
|
|
701
890
|
|
|
@@ -924,8 +1113,9 @@ class TilesetCollisionEditor:
|
|
|
924
1113
|
|
|
925
1114
|
self.handle_event(event)
|
|
926
1115
|
|
|
927
|
-
# Handle toolbar button clicks
|
|
1116
|
+
# Handle toolbar and widget button clicks
|
|
928
1117
|
self._handle_toolbar_button_clicks(events)
|
|
1118
|
+
self._handle_widget_button_clicks(events)
|
|
929
1119
|
|
|
930
1120
|
screen.fill((20, 20, 20))
|
|
931
1121
|
self.draw(screen)
|
|
@@ -166,6 +166,7 @@ src/widgets/regex_automap_designer.py
|
|
|
166
166
|
src/widgets/spritesheet_grid.py
|
|
167
167
|
src/widgets/tile_grid.py
|
|
168
168
|
src/widgets/tile_selector.py
|
|
169
|
+
src/widgets/ui/checkbox.py
|
|
169
170
|
src/widgets/ui/collision_layer_mask.py
|
|
170
171
|
src/widgets/ui/collision_layer_sidebar.py
|
|
171
172
|
src/widgets/ui/confirm_dialog.py
|
|
@@ -333,12 +333,10 @@ class ImagePreview:
|
|
|
333
333
|
new_height = target_height
|
|
334
334
|
new_width = int(target_height * aspect_ratio)
|
|
335
335
|
|
|
336
|
-
source_surface = self.current_image.convert_alpha()
|
|
337
|
-
|
|
338
336
|
scaled_surface = pygame.transform.smoothscale(
|
|
339
|
-
|
|
337
|
+
self.current_image, (new_width, new_height)
|
|
340
338
|
)
|
|
341
|
-
|
|
339
|
+
|
|
342
340
|
self.scaled_cache = scaled_surface
|
|
343
341
|
self.cached_target_size = (target_width, target_height)
|
|
344
342
|
|