tilemap-editor 3.2.7__tar.gz → 3.2.9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tilemap_editor-3.2.7/src/tilemap_editor.egg-info → tilemap_editor-3.2.9}/PKG-INFO +1 -1
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/pyproject.toml +1 -1
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/editor.py +28 -5
- tilemap_editor-3.2.9/src/node_manager.py +147 -0
- tilemap_editor-3.2.9/src/nodes.py +53 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap.py +11 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/settings.py +1 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9/src/tilemap_editor.egg-info}/PKG-INFO +1 -1
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor.egg-info/SOURCES.txt +4 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/ttypes/tilemap.py +1 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/layer_selector.py +63 -1
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/tile_grid.py +244 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/tile_selector.py +21 -0
- tilemap_editor-3.2.9/src/widgets/ui/node_editor.py +337 -0
- tilemap_editor-3.2.9/src/widgets/ui/node_selector.py +602 -0
- tilemap_editor-3.2.9/src/widgets/ui/tileset_type_dialog.py +451 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/ui/toolbar.py +12 -1
- tilemap_editor-3.2.7/src/widgets/ui/tileset_type_dialog.py +0 -214
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/LICENSE +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/MANIFEST.in +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/README.md +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/arrow-down.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/check.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/checked.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/close.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/duplicate.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/error.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/file.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/filedead.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/fit.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/folder.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/grid.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/image.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/info.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/load.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/loop.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/pan.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/pause.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/pencil.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/play.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/plus.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/radio.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/reset.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/save.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/stop.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/tilemap.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/tileset.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/unchecked.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/warning.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/zoomin.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/zoomout.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/setup.cfg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/configs/themes.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/constants.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/event_map.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/layers.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/main.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/__init__.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/character_collision/__init__.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/character_collision/editor.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/character_collision/models.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/character_collision/protocols.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/character_collision/shape_editor.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/character_collision/standalone.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/object_tileset_collision/__init__.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/object_tileset_collision/editor.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/object_tileset_collision/models.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/object_tileset_collision/protocols.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/object_tileset_collision/standalone.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/sprite_animation/__init__.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/sprite_animation/__main__.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/sprite_animation/clipboard_util.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/sprite_animation/editor.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/sprite_animation/frame_picker.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/sprite_animation/models.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/sprite_animation/preview.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/sprite_animation/protocols.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/sprite_animation/runtime_load.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/sprite_animation/standalone.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/sprite_animation/timeline.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/sprite_animation/validation.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/sprite_editor/__init__.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/sprite_editor/editor.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/sprite_editor/standalone.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/tileset_collision/__init__.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/tileset_collision/collision_painter.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/tileset_collision/editor.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/tileset_collision/models.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/tileset_collision/protocols.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/tileset_collision/standalone.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/standalone_automap.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/standalone_error_console.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/standalone_filemanager.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/standalone_image_viewer.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/__init__.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/__main__.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/__init__.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/ToolMove.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/arrow-down.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/check.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/checked.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/close.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/duplicate.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/error.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/file.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/filedead.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/fit.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/folder.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/grid.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/image.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/info.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/load.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/loop.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/pan.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/pause.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/pencil.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/play.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/plus.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/radio.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/reset.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/save.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/stop.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/tilemap.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/tileset.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/unchecked.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/warning.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/zoomin.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/zoomout.svg +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/cli.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/main.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor.egg-info/dependency_links.txt +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor.egg-info/entry_points.txt +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor.egg-info/requires.txt +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor.egg-info/top_level.txt +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/ttypes/__init__.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/utils/__init__.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/utils/editor_preference.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/utils/error_handler.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/utils/font_manager.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/utils/history.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/utils/icon_manager.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/utils/icons_cache.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/utils/log_capture.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/utils/project_paths.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/utils/serialization.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/utils/standalone.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/utils/validation.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/__init__.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/automap_models.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/autotile_template.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/autotiler.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/filemanager.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/input.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/map_properties.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/mapsetup.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/regex_automap_designer.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/spritesheet_grid.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/ui/collision_layer_mask.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/ui/collision_layer_sidebar.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/ui/confirm_dialog.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/ui/draw_utils.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/ui/fileinput.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/ui/layer_type_dialog.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/ui/menubar.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/ui/mode_indicator.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/ui/notification.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/ui/property_editor.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/ui/region_selector.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/ui/status_bar.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/ui/theme.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/ui/tooltip.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/tests/test_animation_library_grid_offset.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/tests/test_collision_layer_mask.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/tests/test_collision_layer_sidebar.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/tests/test_editor_pan_mode.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/tests/test_project_paths.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/tests/test_render_scale.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/tests/test_sprite_animation_editor_grid.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/tests/test_tile_grid_selection.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/tests/test_tile_selector_pick.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/tests/test_tilemap_save.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/tests/test_timeline_invalidate_cache.py +0 -0
- {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/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.2.
|
|
7
|
+
version = "3.2.9"
|
|
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"
|
|
@@ -31,6 +31,8 @@ from widgets.ui.confirm_dialog import ConfirmDialog
|
|
|
31
31
|
from widgets.ui.layer_type_dialog import LayerTypeDialog
|
|
32
32
|
from widgets.ui.menubar import MenuBar
|
|
33
33
|
from widgets.ui.toolbar import Toolbar
|
|
34
|
+
from widgets.ui.node_selector import NodeSelector
|
|
35
|
+
from widgets.ui.node_editor import NodeEditor
|
|
34
36
|
from widgets.ui.notification import NotificationManager
|
|
35
37
|
from widgets.ui.property_editor import PropertyEditor
|
|
36
38
|
from widgets.ui.tooltip import TooltipManager
|
|
@@ -38,6 +40,7 @@ from widgets.ui.theme import get_theme_manager, set_theme, THEMES
|
|
|
38
40
|
from utils.log_capture import setup_console_log
|
|
39
41
|
from utils.standalone import launch_standalone
|
|
40
42
|
from utils import error_handler, error_context
|
|
43
|
+
from node_manager import NodeManager
|
|
41
44
|
|
|
42
45
|
if TYPE_CHECKING:
|
|
43
46
|
from plugins.sprite_animation import SpriteAnimationEditor
|
|
@@ -153,6 +156,7 @@ class Editor:
|
|
|
153
156
|
self.autotile_mode = False
|
|
154
157
|
self.eraser_mode = False
|
|
155
158
|
self.select_mode = False
|
|
159
|
+
self.node_editing_mode = False
|
|
156
160
|
self._prev_tool = None
|
|
157
161
|
|
|
158
162
|
if isinstance(size, tuple) and len(size) == 2:
|
|
@@ -240,6 +244,9 @@ class Editor:
|
|
|
240
244
|
self.map_setup_widget = MapSetup(self, Rect(center_x, center_y, 400, 400))
|
|
241
245
|
self.map_setup_widget.visible = False
|
|
242
246
|
self.map_properties_dialog = MapPropertiesDialog(self, Rect(center_x, center_y, 400, 260))
|
|
247
|
+
self.node_manager = NodeManager(self)
|
|
248
|
+
self.node_selector = NodeSelector(self, 0, 65, 260, 240)
|
|
249
|
+
self.node_editor = NodeEditor(self, 0, 310, 260, 190)
|
|
243
250
|
|
|
244
251
|
def open_file_manager(
|
|
245
252
|
self,
|
|
@@ -1126,7 +1133,7 @@ class Editor:
|
|
|
1126
1133
|
elif event.key == pygame.K_o and (ctrl_held or meta_held):
|
|
1127
1134
|
self.perform_load()
|
|
1128
1135
|
continue
|
|
1129
|
-
elif event.key == pygame.K_SPACE:
|
|
1136
|
+
elif event.key == pygame.K_SPACE and (ctrl_held or meta_held):
|
|
1130
1137
|
if self.pan_mode:
|
|
1131
1138
|
# Restoring: turn off pan, re-enable previous tool
|
|
1132
1139
|
self.pan_mode = False
|
|
@@ -1134,17 +1141,22 @@ class Editor:
|
|
|
1134
1141
|
self.select_mode = True
|
|
1135
1142
|
elif getattr(self, "_prev_tool", None) == "eraser":
|
|
1136
1143
|
self.eraser_mode = True
|
|
1144
|
+
elif getattr(self, "_prev_tool", None) == "nodes":
|
|
1145
|
+
self.node_editing_mode = True
|
|
1137
1146
|
else:
|
|
1138
1147
|
# Entering pan: save current tool, turn off others
|
|
1139
1148
|
if self.select_mode:
|
|
1140
1149
|
self._prev_tool = "select"
|
|
1141
1150
|
elif self.eraser_mode:
|
|
1142
1151
|
self._prev_tool = "eraser"
|
|
1152
|
+
elif self.node_editing_mode:
|
|
1153
|
+
self._prev_tool = "nodes"
|
|
1143
1154
|
else:
|
|
1144
1155
|
self._prev_tool = None
|
|
1145
1156
|
self.pan_mode = True
|
|
1146
1157
|
self.select_mode = False
|
|
1147
1158
|
self.eraser_mode = False
|
|
1159
|
+
self.node_editing_mode = False
|
|
1148
1160
|
continue
|
|
1149
1161
|
elif event.mod & pygame.KMOD_CTRL and event.key == pygame.K_g:
|
|
1150
1162
|
self.toggle_grid()
|
|
@@ -1156,15 +1168,22 @@ class Editor:
|
|
|
1156
1168
|
self.toggle_animation_panel()
|
|
1157
1169
|
continue
|
|
1158
1170
|
elif pygame.K_1 <= event.key <= pygame.K_9:
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
self.tilemap.layer_manager.
|
|
1162
|
-
|
|
1171
|
+
if not (self.node_editor and self.node_editor.visible and self.node_editor.editing_field):
|
|
1172
|
+
idx = event.key - pygame.K_1
|
|
1173
|
+
if idx < self.tilemap.layer_manager.get_layer_count():
|
|
1174
|
+
self.tilemap.layer_manager.set_active_layer(idx)
|
|
1175
|
+
continue
|
|
1163
1176
|
|
|
1164
1177
|
# Priority 5: Toolbar
|
|
1165
1178
|
if self.toolbar and self.toolbar.handle_event(event):
|
|
1166
1179
|
continue
|
|
1167
1180
|
|
|
1181
|
+
# Priority 5.5: Node selector & editor (when node mode active)
|
|
1182
|
+
if self.node_selector and self.node_selector.handle_event(event):
|
|
1183
|
+
continue
|
|
1184
|
+
if self.node_editor and self.node_editor.handle_event(event):
|
|
1185
|
+
continue
|
|
1186
|
+
|
|
1168
1187
|
# Priority 6: Dockable animation panel
|
|
1169
1188
|
if self.left_panel_visible and self.animation_panel:
|
|
1170
1189
|
if hasattr(self.animation_panel, "handle_event"):
|
|
@@ -1303,6 +1322,10 @@ class Editor:
|
|
|
1303
1322
|
|
|
1304
1323
|
if self.toolbar:
|
|
1305
1324
|
self.toolbar.draw(self.screen)
|
|
1325
|
+
if self.node_selector:
|
|
1326
|
+
self.node_selector.draw(self.screen)
|
|
1327
|
+
if self.node_editor:
|
|
1328
|
+
self.node_editor.draw(self.screen)
|
|
1306
1329
|
self.menubar.draw(self.screen)
|
|
1307
1330
|
self.tooltip.draw(self.screen)
|
|
1308
1331
|
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import uuid
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple
|
|
5
|
+
|
|
6
|
+
from nodes import Node, NodeRect
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from editor import Editor
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def create_node_id() -> str:
|
|
13
|
+
return str(uuid.uuid4())
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
SIDECAR_VERSION = 1
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class NodeManager:
|
|
20
|
+
def __init__(self, editor: "Editor") -> None:
|
|
21
|
+
self.editor = editor
|
|
22
|
+
self.nodes: Dict[str, Node] = {}
|
|
23
|
+
self.active_node_id: Optional[str] = None
|
|
24
|
+
self.active_group_name: Optional[str] = None
|
|
25
|
+
self.groups: List[str] = []
|
|
26
|
+
self.default_node_type: str = "area"
|
|
27
|
+
self._nodes_dir: Optional[Path] = None
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def nodes_dir(self) -> Path:
|
|
31
|
+
if self._nodes_dir is None:
|
|
32
|
+
config = getattr(self.editor, "config", {})
|
|
33
|
+
rel = config.get("nodes_path", "nodes")
|
|
34
|
+
self._nodes_dir = self.editor.data_root / rel
|
|
35
|
+
return self._nodes_dir
|
|
36
|
+
|
|
37
|
+
def _sidecar_path_for(self, map_path: Path) -> Path:
|
|
38
|
+
return self.nodes_dir / f"{map_path.stem}.nodes.json"
|
|
39
|
+
|
|
40
|
+
def load(self, map_path: Path) -> None:
|
|
41
|
+
self.nodes.clear()
|
|
42
|
+
self.groups.clear()
|
|
43
|
+
self.active_node_id = None
|
|
44
|
+
self.active_group_name = None
|
|
45
|
+
sidecar = self._sidecar_path_for(map_path)
|
|
46
|
+
if not sidecar.is_file():
|
|
47
|
+
return
|
|
48
|
+
try:
|
|
49
|
+
raw = json.loads(sidecar.read_text(encoding="utf-8"))
|
|
50
|
+
if not isinstance(raw, dict):
|
|
51
|
+
return
|
|
52
|
+
self.groups = list(raw.get("groups", []))
|
|
53
|
+
for item in raw.get("nodes", []):
|
|
54
|
+
if isinstance(item, dict):
|
|
55
|
+
node = Node.from_dict(item)
|
|
56
|
+
self.nodes[node.node_id] = node
|
|
57
|
+
except (json.JSONDecodeError, KeyError, ValueError, TypeError):
|
|
58
|
+
import logging
|
|
59
|
+
logging.warning(f"Failed to load nodes from {sidecar}")
|
|
60
|
+
|
|
61
|
+
def save(self, map_path: Path) -> None:
|
|
62
|
+
sidecar = self._sidecar_path_for(map_path)
|
|
63
|
+
self.nodes_dir.mkdir(parents=True, exist_ok=True)
|
|
64
|
+
data: Dict[str, Any] = {
|
|
65
|
+
"version": SIDECAR_VERSION,
|
|
66
|
+
"groups": self.groups,
|
|
67
|
+
"nodes": [node.to_dict() for node in self.nodes.values()],
|
|
68
|
+
}
|
|
69
|
+
sidecar.write_text(json.dumps(data, indent=2), encoding="utf-8")
|
|
70
|
+
|
|
71
|
+
def add_node(self, node: Node) -> str:
|
|
72
|
+
self.nodes[node.node_id] = node
|
|
73
|
+
return node.node_id
|
|
74
|
+
|
|
75
|
+
def remove_node(self, node_id: str) -> None:
|
|
76
|
+
self.nodes.pop(node_id, None)
|
|
77
|
+
if self.active_node_id == node_id:
|
|
78
|
+
self.active_node_id = None
|
|
79
|
+
|
|
80
|
+
def get_node(self, node_id: str) -> Optional[Node]:
|
|
81
|
+
return self.nodes.get(node_id)
|
|
82
|
+
|
|
83
|
+
def get_active_node(self) -> Optional[Node]:
|
|
84
|
+
if self.active_node_id is None:
|
|
85
|
+
return None
|
|
86
|
+
return self.nodes.get(self.active_node_id)
|
|
87
|
+
|
|
88
|
+
def set_active_node(self, node_id: Optional[str]) -> None:
|
|
89
|
+
self.active_node_id = node_id
|
|
90
|
+
if node_id is not None:
|
|
91
|
+
self.active_group_name = None
|
|
92
|
+
|
|
93
|
+
def set_active_group(self, group_name: Optional[str]) -> None:
|
|
94
|
+
self.active_group_name = group_name
|
|
95
|
+
if group_name is not None:
|
|
96
|
+
self.active_node_id = None
|
|
97
|
+
|
|
98
|
+
def get_nodes_for_layer(self, layer_name: str) -> List[Node]:
|
|
99
|
+
return [n for n in self.nodes.values() if n.layer_name == layer_name]
|
|
100
|
+
|
|
101
|
+
def create_default_node(self, layer_name: str, node_type: str = "area") -> Node:
|
|
102
|
+
count = len(self.nodes) + 1
|
|
103
|
+
return Node(
|
|
104
|
+
node_id=create_node_id(),
|
|
105
|
+
name=f"{node_type.capitalize()} {count}",
|
|
106
|
+
node_type=node_type,
|
|
107
|
+
area=NodeRect(x=0, y=0, w=64, h=64),
|
|
108
|
+
layer_name=layer_name,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
def rename_group(self, old_name: str, new_name: str) -> bool:
|
|
112
|
+
if not new_name or new_name == old_name:
|
|
113
|
+
return False
|
|
114
|
+
if new_name in self.groups:
|
|
115
|
+
return False # Already exists
|
|
116
|
+
if old_name not in self.groups:
|
|
117
|
+
return False
|
|
118
|
+
|
|
119
|
+
# Update groups list
|
|
120
|
+
idx = self.groups.index(old_name)
|
|
121
|
+
self.groups[idx] = new_name
|
|
122
|
+
|
|
123
|
+
# Update child nodes
|
|
124
|
+
for node in self.nodes.values():
|
|
125
|
+
if node.group == old_name:
|
|
126
|
+
node.group = new_name
|
|
127
|
+
|
|
128
|
+
if self.active_group_name == old_name:
|
|
129
|
+
self.active_group_name = new_name
|
|
130
|
+
return True
|
|
131
|
+
|
|
132
|
+
def reorder_node(self, node_id: str, target_node_id: str, before: bool = True) -> None:
|
|
133
|
+
if node_id not in self.nodes or target_node_id not in self.nodes:
|
|
134
|
+
return
|
|
135
|
+
node = self.nodes[node_id]
|
|
136
|
+
keys = list(self.nodes.keys())
|
|
137
|
+
keys.remove(node_id)
|
|
138
|
+
target_idx = keys.index(target_node_id)
|
|
139
|
+
if not before:
|
|
140
|
+
target_idx += 1
|
|
141
|
+
keys.insert(target_idx, node_id)
|
|
142
|
+
|
|
143
|
+
# Rebuild dictionary
|
|
144
|
+
new_nodes = {}
|
|
145
|
+
for k in keys:
|
|
146
|
+
new_nodes[k] = self.nodes[k]
|
|
147
|
+
self.nodes = new_nodes
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import Any, Dict, Optional
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class NodeRect:
|
|
9
|
+
x: int
|
|
10
|
+
y: int
|
|
11
|
+
w: int
|
|
12
|
+
h: int
|
|
13
|
+
|
|
14
|
+
def to_dict(self) -> Dict[str, int]:
|
|
15
|
+
return {"x": self.x, "y": self.y, "w": self.w, "h": self.h}
|
|
16
|
+
|
|
17
|
+
@classmethod
|
|
18
|
+
def from_dict(cls, data: Dict[str, Any]) -> "NodeRect":
|
|
19
|
+
return cls(x=int(data["x"]), y=int(data["y"]), w=int(data["w"]), h=int(data["h"]))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class Node:
|
|
24
|
+
node_id: str
|
|
25
|
+
name: str
|
|
26
|
+
node_type: str
|
|
27
|
+
area: NodeRect
|
|
28
|
+
layer_name: str
|
|
29
|
+
properties: Dict[str, Any] = field(default_factory=dict)
|
|
30
|
+
group: Optional[str] = None
|
|
31
|
+
|
|
32
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
33
|
+
return {
|
|
34
|
+
"node_id": self.node_id,
|
|
35
|
+
"name": self.name,
|
|
36
|
+
"node_type": self.node_type,
|
|
37
|
+
"area": self.area.to_dict(),
|
|
38
|
+
"layer_name": self.layer_name,
|
|
39
|
+
"properties": dict(self.properties),
|
|
40
|
+
"group": self.group,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@classmethod
|
|
44
|
+
def from_dict(cls, data: Dict[str, Any]) -> "Node":
|
|
45
|
+
return cls(
|
|
46
|
+
node_id=str(data["node_id"]),
|
|
47
|
+
name=str(data["name"]),
|
|
48
|
+
node_type=str(data.get("node_type", "area")),
|
|
49
|
+
area=NodeRect.from_dict(data["area"]),
|
|
50
|
+
layer_name=str(data.get("layer_name", "")),
|
|
51
|
+
properties=dict(data.get("properties", {})),
|
|
52
|
+
group=data.get("group"),
|
|
53
|
+
)
|
|
@@ -298,6 +298,8 @@ class Tilemap:
|
|
|
298
298
|
ts_data["tile_properties"] = {
|
|
299
299
|
str(k): v for k, v in ts.tile_properties.items()
|
|
300
300
|
}
|
|
301
|
+
if ts.animation:
|
|
302
|
+
ts_data["animation"] = ts.animation
|
|
301
303
|
|
|
302
304
|
save_data["resources"]["tilesets"].append(ts_data)
|
|
303
305
|
|
|
@@ -394,6 +396,9 @@ class Tilemap:
|
|
|
394
396
|
with open(target_path, "w") as f:
|
|
395
397
|
JSONDump(save_data, f, indent=2)
|
|
396
398
|
|
|
399
|
+
if hasattr(self.editor, "node_manager"):
|
|
400
|
+
self.editor.node_manager.save(target_path)
|
|
401
|
+
|
|
397
402
|
print(f"Saved to {target_path}")
|
|
398
403
|
|
|
399
404
|
def _project_base_path(self) -> Path:
|
|
@@ -544,6 +549,7 @@ class Tilemap:
|
|
|
544
549
|
tileset_type,
|
|
545
550
|
properties=ts_entry.get("properties", {}),
|
|
546
551
|
tile_properties=ts_entry.get("tile_properties", {}),
|
|
552
|
+
animation=ts_entry.get("animation"),
|
|
547
553
|
)
|
|
548
554
|
except Exception as e:
|
|
549
555
|
error_msg = f"Error loading tileset {path_str}: {e}"
|
|
@@ -654,6 +660,11 @@ class Tilemap:
|
|
|
654
660
|
if active_layer:
|
|
655
661
|
active_layer.tiles[pos] = tile_data
|
|
656
662
|
|
|
663
|
+
if hasattr(self.editor, "node_manager"):
|
|
664
|
+
self.editor.node_manager.load(path)
|
|
665
|
+
if hasattr(self.editor, "node_selector"):
|
|
666
|
+
self.editor.node_selector._rebuild_filter()
|
|
667
|
+
|
|
657
668
|
self.initialized = True
|
|
658
669
|
|
|
659
670
|
def _object_tileset_indices_from_payload(self, payload: dict) -> set[int]:
|
|
@@ -45,6 +45,8 @@ src/editor.py
|
|
|
45
45
|
src/event_map.py
|
|
46
46
|
src/layers.py
|
|
47
47
|
src/main.py
|
|
48
|
+
src/node_manager.py
|
|
49
|
+
src/nodes.py
|
|
48
50
|
src/standalone_automap.py
|
|
49
51
|
src/standalone_error_console.py
|
|
50
52
|
src/standalone_filemanager.py
|
|
@@ -170,6 +172,8 @@ src/widgets/ui/fileinput.py
|
|
|
170
172
|
src/widgets/ui/layer_type_dialog.py
|
|
171
173
|
src/widgets/ui/menubar.py
|
|
172
174
|
src/widgets/ui/mode_indicator.py
|
|
175
|
+
src/widgets/ui/node_editor.py
|
|
176
|
+
src/widgets/ui/node_selector.py
|
|
173
177
|
src/widgets/ui/notification.py
|
|
174
178
|
src/widgets/ui/property_editor.py
|
|
175
179
|
src/widgets/ui/region_selector.py
|
|
@@ -64,6 +64,7 @@ class TypeTilesetSerialized(TypedDict, total=True):
|
|
|
64
64
|
type: str
|
|
65
65
|
properties: NotRequired[Dict[str, Any]]
|
|
66
66
|
tile_properties: NotRequired[Dict[str, Dict[str, Any]]] # variant_id string -> properties
|
|
67
|
+
animation: NotRequired[dict]
|
|
67
68
|
|
|
68
69
|
|
|
69
70
|
TTile = Dict[Tuple[int, int], TypeTile]
|
|
@@ -44,6 +44,8 @@ class LayerSelector:
|
|
|
44
44
|
self.rename_text: str = ""
|
|
45
45
|
self.rename_original_name: str = ""
|
|
46
46
|
|
|
47
|
+
self._adjusting_opacity_idx: Optional[int] = None
|
|
48
|
+
|
|
47
49
|
btn_h = 25
|
|
48
50
|
btn_w = 25
|
|
49
51
|
btn_y = self.footer_rect.y + 5
|
|
@@ -113,6 +115,19 @@ class LayerSelector:
|
|
|
113
115
|
layer.locked = not layer.locked
|
|
114
116
|
return True
|
|
115
117
|
|
|
118
|
+
opacity_rect = self._get_opacity_bar_rect(layer_idx)
|
|
119
|
+
if opacity_rect and opacity_rect.collidepoint(mouse_pos):
|
|
120
|
+
layer = self.editor.tilemap.layer_manager.get_layer(
|
|
121
|
+
layer_idx
|
|
122
|
+
)
|
|
123
|
+
if layer:
|
|
124
|
+
self._adjusting_opacity_idx = layer_idx
|
|
125
|
+
rel_x = mouse_pos[0] - opacity_rect.x
|
|
126
|
+
layer.opacity = max(
|
|
127
|
+
0.0, min(1.0, rel_x / opacity_rect.width)
|
|
128
|
+
)
|
|
129
|
+
return True
|
|
130
|
+
|
|
116
131
|
self.dragging_layer_idx = layer_idx
|
|
117
132
|
self.drag_start_y = mouse_pos[1]
|
|
118
133
|
|
|
@@ -153,6 +168,9 @@ class LayerSelector:
|
|
|
153
168
|
return True
|
|
154
169
|
|
|
155
170
|
elif event.type == pygame.MOUSEBUTTONUP:
|
|
171
|
+
if event.button == 1:
|
|
172
|
+
self._adjusting_opacity_idx = None
|
|
173
|
+
|
|
156
174
|
if event.button == 1 and self.dragging_layer_idx is not None:
|
|
157
175
|
layer_idx = self._get_layer_at_pos(mouse_pos)
|
|
158
176
|
if layer_idx is not None and layer_idx != self.dragging_layer_idx:
|
|
@@ -164,6 +182,19 @@ class LayerSelector:
|
|
|
164
182
|
return True
|
|
165
183
|
|
|
166
184
|
elif event.type == pygame.MOUSEMOTION:
|
|
185
|
+
if self._adjusting_opacity_idx is not None:
|
|
186
|
+
layer = self.editor.tilemap.layer_manager.get_layer(
|
|
187
|
+
self._adjusting_opacity_idx
|
|
188
|
+
)
|
|
189
|
+
if layer:
|
|
190
|
+
opacity_rect = self._get_opacity_bar_rect(
|
|
191
|
+
self._adjusting_opacity_idx
|
|
192
|
+
)
|
|
193
|
+
if opacity_rect:
|
|
194
|
+
rel_x = max(0, min(opacity_rect.width, mouse_pos[0] - opacity_rect.x))
|
|
195
|
+
layer.opacity = max(0.0, min(1.0, rel_x / opacity_rect.width))
|
|
196
|
+
return True
|
|
197
|
+
|
|
167
198
|
if self.list_rect.collidepoint(mouse_pos):
|
|
168
199
|
if self.dragging_layer_idx is None:
|
|
169
200
|
self.hover_idx = self._get_layer_at_pos(mouse_pos)
|
|
@@ -188,7 +219,13 @@ class LayerSelector:
|
|
|
188
219
|
self._cancel_rename()
|
|
189
220
|
return True
|
|
190
221
|
elif event.key == pygame.K_BACKSPACE:
|
|
191
|
-
|
|
222
|
+
pressed = pygame.key.get_pressed()
|
|
223
|
+
meta_down = pressed[pygame.K_LMETA] or pressed[pygame.K_RMETA]
|
|
224
|
+
ctrl_down = pressed[pygame.K_LCTRL] or pressed[pygame.K_RCTRL]
|
|
225
|
+
if meta_down or ctrl_down:
|
|
226
|
+
self.rename_text = ""
|
|
227
|
+
else:
|
|
228
|
+
self.rename_text = self.rename_text[:-1]
|
|
192
229
|
return True
|
|
193
230
|
else:
|
|
194
231
|
if event.unicode.isprintable():
|
|
@@ -219,6 +256,19 @@ class LayerSelector:
|
|
|
219
256
|
|
|
220
257
|
return None
|
|
221
258
|
|
|
259
|
+
def _get_opacity_bar_rect(self, layer_idx: int) -> Optional[Rect]:
|
|
260
|
+
"""Get the clickable rect for the opacity bar of a layer."""
|
|
261
|
+
item_y = self.list_rect.y + (layer_idx * self.item_h) - self.scroll_offset
|
|
262
|
+
|
|
263
|
+
if item_y + self.item_h < self.list_rect.y or item_y > self.list_rect.bottom:
|
|
264
|
+
return None
|
|
265
|
+
|
|
266
|
+
bar_w = 46
|
|
267
|
+
bar_h = 6
|
|
268
|
+
bar_x = self.list_rect.x + self.list_rect.width - 80
|
|
269
|
+
bar_y = item_y + self.item_h - bar_h - 3
|
|
270
|
+
return Rect(bar_x, bar_y, bar_w, bar_h)
|
|
271
|
+
|
|
222
272
|
def _get_eye_icon_rect(self, layer_idx: int, mouse_pos) -> Optional[Rect]:
|
|
223
273
|
"""Get the clickable rect for the eye icon of a layer."""
|
|
224
274
|
if layer_idx is None:
|
|
@@ -404,6 +454,18 @@ class LayerSelector:
|
|
|
404
454
|
name_txt = self.font_layer.render(layer.name, True, self.text_color)
|
|
405
455
|
screen.blit(name_txt, (item_rect.x + 5, item_rect.y + 5))
|
|
406
456
|
|
|
457
|
+
opacity_bar = self._get_opacity_bar_rect(i)
|
|
458
|
+
if opacity_bar:
|
|
459
|
+
bg_rect = Rect(opacity_bar.x, opacity_bar.y, opacity_bar.width, opacity_bar.height)
|
|
460
|
+
pygame.draw.rect(screen, (60, 60, 60), bg_rect, border_radius=2)
|
|
461
|
+
fill_w = int(opacity_bar.width * layer.opacity)
|
|
462
|
+
if fill_w > 0:
|
|
463
|
+
fill_rect = Rect(opacity_bar.x, opacity_bar.y, fill_w, opacity_bar.height)
|
|
464
|
+
green = int(180 * layer.opacity) + 40
|
|
465
|
+
pygame.draw.rect(screen, (40, green, 40), fill_rect, border_radius=2)
|
|
466
|
+
pct_txt = self.font_layer.render(f"{int(layer.opacity * 100)}%", True, self.text_muted)
|
|
467
|
+
screen.blit(pct_txt, (opacity_bar.x - 32, opacity_bar.y - 2))
|
|
468
|
+
|
|
407
469
|
eye_x = item_rect.right - 25
|
|
408
470
|
eye_y = item_rect.y + 7
|
|
409
471
|
if layer.visible:
|