tilemap-editor 4.3.9__tar.gz → 4.4.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-4.3.9 → tilemap_editor-4.4.0}/CHANGELOG.md +10 -0
- {tilemap_editor-4.3.9/src/tilemap_editor.egg-info → tilemap_editor-4.4.0}/PKG-INFO +1 -1
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/pyproject.toml +1 -1
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/editor.py +2 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0/src/tilemap_editor.egg-info}/PKG-INFO +1 -1
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor.egg-info/SOURCES.txt +3 -0
- tilemap_editor-4.4.0/src/utils/context_dispatch.py +83 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/layer_selector.py +19 -11
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/tile_grid.py +40 -2
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/tile_selector.py +88 -17
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/ui/button.py +17 -3
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/ui/node_editor.py +20 -8
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/ui/property_editor.py +76 -20
- tilemap_editor-4.4.0/tests/test_context_dispatch.py +287 -0
- tilemap_editor-4.4.0/tests/test_object_properties_context.py +123 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/tests/test_tile_selector_pick.py +41 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/LICENSE +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/MANIFEST.in +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/README.md +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/arrow-down.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/check.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/checked.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/close.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/duplicate.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/error.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/file.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/filedead.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/fit.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/folder.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/grid.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/image.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/info.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/load.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/loop.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/onion.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/pan.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/pause.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/pencil.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/play.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/plus.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/radio.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/reset.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/save.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/skip-back.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/skip-forward.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/stop.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/tilemap.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/tileset.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/unchecked.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/warning.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/zoomin.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/assets/icons/zoomout.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/setup.cfg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/constants.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/event_map.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/layers.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/main.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/node_manager.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/nodes.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/__init__.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/character_collision/__init__.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/character_collision/editor.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/character_collision/models.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/character_collision/protocols.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/character_collision/shape_editor.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/character_collision/standalone.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/object_tileset_collision/__init__.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/object_tileset_collision/editor.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/object_tileset_collision/models.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/object_tileset_collision/protocols.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/object_tileset_collision/standalone.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/sprite_animation/__init__.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/sprite_animation/__main__.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/sprite_animation/clipboard_util.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/sprite_animation/editor.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/sprite_animation/frame_picker.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/sprite_animation/models.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/sprite_animation/preview.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/sprite_animation/protocols.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/sprite_animation/runtime_load.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/sprite_animation/standalone.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/sprite_animation/timeline.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/sprite_animation/validation.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/sprite_editor/__init__.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/sprite_editor/camera.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/sprite_editor/clipboard.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/sprite_editor/commands.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/sprite_editor/dialogs.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/sprite_editor/document.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/sprite_editor/editor.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/sprite_editor/overlays.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/sprite_editor/region_export.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/sprite_editor/selection.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/sprite_editor/standalone.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/sprite_editor/tools.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/sprite_editor/viewport.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/tileset_collision/__init__.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/tileset_collision/collision_painter.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/tileset_collision/editor.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/tileset_collision/models.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/tileset_collision/protocols.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/plugins/tileset_collision/standalone.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/run.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/standalone_automap.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/standalone_error_console.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/standalone_filemanager.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/standalone_image_viewer.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/__init__.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/__main__.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/__init__.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/ToolMove.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/arrow-down.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/check.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/checked.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/close.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/duplicate.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/error.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/file.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/filedead.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/fit.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/fold_down_arrow.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/folder.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/gear.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/grid.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/image.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/info.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/load.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/loop.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/miniobj.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/onion.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/pan.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/pause.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/pencil.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/play.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/plus.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/radio.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/reset.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/save.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/skip-back.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/skip-forward.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/stop.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/tilemap.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/tileset.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/unchecked.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/unfold_right_arrow.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/warning.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/zoomin.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/zoomout.svg +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/cli.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/main.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor/settings.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor.egg-info/dependency_links.txt +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor.egg-info/entry_points.txt +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor.egg-info/requires.txt +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/tilemap_editor.egg-info/top_level.txt +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/ttypes/__init__.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/ttypes/tilemap.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/utils/__init__.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/utils/editor_preference.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/utils/error_handler.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/utils/font_manager.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/utils/history.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/utils/icon_manager.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/utils/icons_cache.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/utils/log_capture.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/utils/natural_sort.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/utils/project_paths.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/utils/property_suggestions.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/utils/serialization.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/utils/standalone.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/utils/validation.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/__init__.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/automap_models.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/autotile_template.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/autotiler.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/filemanager.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/input.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/map_properties.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/mapsetup.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/particle_presets.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/particle_system.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/regex_automap_designer.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/ui/checkbox.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/ui/collision_layer_mask.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/ui/collision_layer_sidebar.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/ui/confirm_dialog.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/ui/dialog_base.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/ui/drag_tracker.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/ui/draw_utils.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/ui/fileinput.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/ui/label.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/ui/layer_type_dialog.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/ui/menubar.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/ui/mode_indicator.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/ui/node_selector.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/ui/notification.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/ui/particle_config_dialog.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/ui/region_selector.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/ui/resize_map_dialog.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/ui/scrollbar.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/ui/sidebar_container.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/ui/splitter.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/ui/status_bar.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/ui/theme.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/ui/tileset_type_dialog.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/ui/toast.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/ui/tool_manager.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/ui/toolbar.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/ui/tooltip.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/ui/tree_widget.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/src/widgets/widget_base.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/tests/test_animation_library_grid_offset.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/tests/test_collision_layer_mask.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/tests/test_collision_layer_sidebar.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/tests/test_drag_tracker.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/tests/test_editor_pan_mode.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/tests/test_gid_collision.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/tests/test_layers_autotile.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/tests/test_map_resize_model.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/tests/test_natural_sort.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/tests/test_project_paths.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/tests/test_render_scale.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/tests/test_serialization_regression.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/tests/test_sprite_animation_editor_grid.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/tests/test_sprite_editor_commands.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/tests/test_sprite_editor_interaction.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/tests/test_sprite_editor_models.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/tests/test_tile_grid_selection.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/tests/test_tilemap_save.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/tests/test_timeline_invalidate_cache.py +0 -0
- {tilemap_editor-4.3.9 → tilemap_editor-4.4.0}/tests/test_toolbar_tools.py +0 -0
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
3
|
<!-- towncrier release notes start -->
|
|
4
|
+
# tilemap_editor 4.4.0 (2026-08-19)
|
|
5
|
+
|
|
6
|
+
## Features
|
|
7
|
+
|
|
8
|
+
- Property editor: added a remove button for individual properties and object-level property support. Right-clicking a map object, tileset image, or tree row now opens the property editor through a context dispatch system, and object properties can be edited on the placed object directly. (context-property-editor)
|
|
9
|
+
|
|
10
|
+
## Bug Fixes
|
|
11
|
+
|
|
12
|
+
- Object picking on object layers now hits the visually topmost overlapping object: right-click property open and left-click variant select test objects in reverse draw order. (object-pick-order)
|
|
13
|
+
|
|
4
14
|
# tilemap_editor 4.3.9 (2026-08-04)
|
|
5
15
|
|
|
6
16
|
## Bug Fixes
|
|
@@ -21,6 +21,7 @@ from constants import BASE_PATH
|
|
|
21
21
|
from node_manager import NodeManager
|
|
22
22
|
from tilemap import Tilemap
|
|
23
23
|
from utils import error_context, error_handler
|
|
24
|
+
from utils.context_dispatch import PropertyContextDispatcher
|
|
24
25
|
from utils.property_suggestions import PropertySuggestionRegistry
|
|
25
26
|
from utils.standalone import launch_standalone
|
|
26
27
|
from widgets.autotiler import AutotileRuleDesigner
|
|
@@ -214,6 +215,7 @@ class Editor:
|
|
|
214
215
|
self.notifications = NotificationManager(self)
|
|
215
216
|
self.tooltip = TooltipManager()
|
|
216
217
|
self.suggestion_registry = PropertySuggestionRegistry()
|
|
218
|
+
self.context_dispatch = PropertyContextDispatcher()
|
|
217
219
|
self.error_console_process: subprocess.Popen | None = None
|
|
218
220
|
self.property_editor: PropertyEditor | None = None
|
|
219
221
|
self.particle_config_dialog: ParticleConfigDialog | None = None
|
|
@@ -161,6 +161,7 @@ src/tilemap_editor/assets/icons/zoomout.svg
|
|
|
161
161
|
src/ttypes/__init__.py
|
|
162
162
|
src/ttypes/tilemap.py
|
|
163
163
|
src/utils/__init__.py
|
|
164
|
+
src/utils/context_dispatch.py
|
|
164
165
|
src/utils/editor_preference.py
|
|
165
166
|
src/utils/error_handler.py
|
|
166
167
|
src/utils/font_manager.py
|
|
@@ -223,12 +224,14 @@ src/widgets/ui/tree_widget.py
|
|
|
223
224
|
tests/test_animation_library_grid_offset.py
|
|
224
225
|
tests/test_collision_layer_mask.py
|
|
225
226
|
tests/test_collision_layer_sidebar.py
|
|
227
|
+
tests/test_context_dispatch.py
|
|
226
228
|
tests/test_drag_tracker.py
|
|
227
229
|
tests/test_editor_pan_mode.py
|
|
228
230
|
tests/test_gid_collision.py
|
|
229
231
|
tests/test_layers_autotile.py
|
|
230
232
|
tests/test_map_resize_model.py
|
|
231
233
|
tests/test_natural_sort.py
|
|
234
|
+
tests/test_object_properties_context.py
|
|
232
235
|
tests/test_project_paths.py
|
|
233
236
|
tests/test_render_scale.py
|
|
234
237
|
tests/test_serialization_regression.py
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""Context-based property event dispatch.
|
|
2
|
+
|
|
3
|
+
Godot-style "context slots": widgets emit a ``PropertyContext`` describing
|
|
4
|
+
what was clicked (right-click on map object, tileset image, tree row, ...);
|
|
5
|
+
the dispatcher routes it to the registered opener (shows the PropertyEditor)
|
|
6
|
+
or saver (applies the saved properties to the context target).
|
|
7
|
+
|
|
8
|
+
Same input event everywhere, the context decides the polymorphic handler.
|
|
9
|
+
Unregistered kinds no-op cleanly (Qt-style propagation fallback).
|
|
10
|
+
|
|
11
|
+
Example::
|
|
12
|
+
|
|
13
|
+
editor.context_dispatch.open(PropertyContext(ContextKind.TILESET, ts))
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from collections.abc import Callable
|
|
17
|
+
from dataclasses import dataclass, field
|
|
18
|
+
from enum import Enum, auto
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ContextKind(Enum):
|
|
23
|
+
"""What was clicked / is being edited."""
|
|
24
|
+
|
|
25
|
+
TILESET = auto() # whole-tileset properties
|
|
26
|
+
TILE_VARIANT = auto() # per-tile properties (grid tilesets)
|
|
27
|
+
MAP_OBJECT = auto() # placed object on an object layer
|
|
28
|
+
NODE = auto() # node (area / particle_emitter / group)
|
|
29
|
+
LAYER = auto() # layer
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class PropertyContext:
|
|
34
|
+
"""Context for an open/save property event.
|
|
35
|
+
|
|
36
|
+
``target`` is the typed object the event refers to (TilesetData, Node,
|
|
37
|
+
Layer, ...). ``extra`` carries kind-specific data (e.g. variant_ids).
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
kind: ContextKind
|
|
41
|
+
target: Any = None
|
|
42
|
+
extra: dict[str, Any] = field(default_factory=dict)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
Opener = Callable[[PropertyContext], None]
|
|
46
|
+
Saver = Callable[[PropertyContext, dict[str, Any]], None]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# TODO(dispatcher): temporary shared callback channel. Planned refactor: route
|
|
50
|
+
# property open/save through the Editor reference (self.editor) and direct
|
|
51
|
+
# widget/manager calls instead of registered openers/savers.
|
|
52
|
+
class PropertyContextDispatcher:
|
|
53
|
+
"""Routes property open/save events by context kind.
|
|
54
|
+
|
|
55
|
+
One registered handler per kind; dispatch is a no-op for unregistered
|
|
56
|
+
kinds so new contexts can be introduced without breaking old ones.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
def __init__(self):
|
|
60
|
+
self._openers: dict[ContextKind, Opener] = {}
|
|
61
|
+
self._savers: dict[ContextKind, Saver] = {}
|
|
62
|
+
|
|
63
|
+
def register_opener(self, kind: ContextKind, opener: Opener) -> None:
|
|
64
|
+
self._openers[kind] = opener
|
|
65
|
+
|
|
66
|
+
def register_saver(self, kind: ContextKind, saver: Saver) -> None:
|
|
67
|
+
self._savers[kind] = saver
|
|
68
|
+
|
|
69
|
+
def open(self, ctx: PropertyContext) -> bool:
|
|
70
|
+
"""Show the property editor for the context. Returns True if handled."""
|
|
71
|
+
opener = self._openers.get(ctx.kind)
|
|
72
|
+
if opener is None:
|
|
73
|
+
return False
|
|
74
|
+
opener(ctx)
|
|
75
|
+
return True
|
|
76
|
+
|
|
77
|
+
def save(self, ctx: PropertyContext, props: dict[str, Any]) -> bool:
|
|
78
|
+
"""Apply saved properties to the context target. Returns True if handled."""
|
|
79
|
+
saver = self._savers.get(ctx.kind)
|
|
80
|
+
if saver is None:
|
|
81
|
+
return False
|
|
82
|
+
saver(ctx, props)
|
|
83
|
+
return True
|
|
@@ -8,7 +8,7 @@ from typing import TYPE_CHECKING
|
|
|
8
8
|
import pygame
|
|
9
9
|
from pygame import Rect, Surface
|
|
10
10
|
|
|
11
|
-
from
|
|
11
|
+
from utils.context_dispatch import ContextKind, PropertyContext
|
|
12
12
|
from widgets.ui.button import Button
|
|
13
13
|
from widgets.ui.property_editor import PropertyEditor
|
|
14
14
|
from widgets.ui.theme import COLORS, FONTS, SHAPE
|
|
@@ -65,6 +65,10 @@ class LayerSelector:
|
|
|
65
65
|
self.font_header = FONTS.get_bold_font(FONTS.size_md)
|
|
66
66
|
self.font_layer = FONTS.get_small_font()
|
|
67
67
|
|
|
68
|
+
d = self.editor.context_dispatch
|
|
69
|
+
d.register_opener(ContextKind.LAYER, self._open_layer_properties)
|
|
70
|
+
d.register_saver(ContextKind.LAYER, self._save_layer_properties)
|
|
71
|
+
|
|
68
72
|
def resize(self, x: int, y: int, w: int, h: int):
|
|
69
73
|
self.rect = Rect(x, y, w, h)
|
|
70
74
|
self.header_rect = Rect(x, y, w, self.header_h)
|
|
@@ -149,14 +153,8 @@ class LayerSelector:
|
|
|
149
153
|
if layer_idx is not None:
|
|
150
154
|
layer = self.editor.tilemap.layer_manager.get_layer(layer_idx)
|
|
151
155
|
if layer:
|
|
152
|
-
self.editor.
|
|
153
|
-
|
|
154
|
-
f"Layer Properties: {layer.name}",
|
|
155
|
-
layer.properties,
|
|
156
|
-
on_save=lambda props: self._save_layer_properties(
|
|
157
|
-
layer, props
|
|
158
|
-
),
|
|
159
|
-
on_close=lambda: None,
|
|
156
|
+
self.editor.context_dispatch.open(
|
|
157
|
+
PropertyContext(ContextKind.LAYER, layer)
|
|
160
158
|
)
|
|
161
159
|
return True
|
|
162
160
|
|
|
@@ -406,7 +404,17 @@ class LayerSelector:
|
|
|
406
404
|
self.rename_text = ""
|
|
407
405
|
self.rename_original_name = ""
|
|
408
406
|
|
|
409
|
-
def
|
|
407
|
+
def _open_layer_properties(self, ctx: PropertyContext) -> None:
|
|
408
|
+
layer = ctx.target
|
|
409
|
+
self.editor.property_editor = PropertyEditor(
|
|
410
|
+
self.editor,
|
|
411
|
+
f"Layer Properties: {layer.name}",
|
|
412
|
+
layer.properties,
|
|
413
|
+
context=ctx,
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
def _save_layer_properties(self, ctx: PropertyContext, props: dict):
|
|
417
|
+
layer = ctx.target
|
|
410
418
|
layer.properties = props
|
|
411
419
|
self.editor.suggestion_registry.refresh(self.editor)
|
|
412
420
|
print(f"Saved properties for layer: {layer.name}")
|
|
@@ -547,7 +555,7 @@ class LayerSelector:
|
|
|
547
555
|
if ysort_rect:
|
|
548
556
|
ysort_color = COLORS.accent if layer.y_sort else COLORS.text_dim
|
|
549
557
|
pygame.draw.rect(
|
|
550
|
-
screen, ysort_color, ysort_rect,
|
|
558
|
+
screen, ysort_color, ysort_rect,
|
|
551
559
|
border_radius=SHAPE.radius_sm,
|
|
552
560
|
)
|
|
553
561
|
ysort_txt = self.font_layer.render("Y", True, COLORS.text)
|
|
@@ -5,6 +5,7 @@ from pygame import K_LEFT, K_RIGHT, K_UP, Rect, Surface
|
|
|
5
5
|
|
|
6
6
|
from nodes import NodeRect
|
|
7
7
|
from ttypes.tilemap import TypeObject, TypeTile
|
|
8
|
+
from utils.context_dispatch import ContextKind, PropertyContext
|
|
8
9
|
from widgets.particle_system import MAX_DT, ParticlePreview
|
|
9
10
|
from widgets.ui.drag_tracker import DragTracker
|
|
10
11
|
from widgets.ui.theme import COLORS
|
|
@@ -429,7 +430,7 @@ class TileGrid:
|
|
|
429
430
|
return True
|
|
430
431
|
|
|
431
432
|
if event.button == 3:
|
|
432
|
-
if is_hovering and self.hover_cell:
|
|
433
|
+
if is_hovering and self.hover_cell and not self._open_object_properties_if_hit(mouse_pos):
|
|
433
434
|
self.pick_tile_at(self.hover_cell)
|
|
434
435
|
return True
|
|
435
436
|
|
|
@@ -777,7 +778,7 @@ class TileGrid:
|
|
|
777
778
|
world_pos = self.screen_to_world(mouse_pos)
|
|
778
779
|
rs = self.editor.tilemap.render_scale
|
|
779
780
|
pick_pos = (int(world_pos[0] / rs), int(world_pos[1] / rs)) if rs else world_pos
|
|
780
|
-
for _obj_id, obj in active_layer.get_all_objects().items():
|
|
781
|
+
for _obj_id, obj in reversed(list(active_layer.get_all_objects().items())):
|
|
781
782
|
area = obj["area"]
|
|
782
783
|
obj_rect = Rect(area["x"], area["y"], area["w"], area["h"])
|
|
783
784
|
if obj_rect.collidepoint(pick_pos):
|
|
@@ -786,6 +787,43 @@ class TileGrid:
|
|
|
786
787
|
ts_widget.select_tile_by_variant(ttype, variant)
|
|
787
788
|
return
|
|
788
789
|
|
|
790
|
+
def _open_object_properties_if_hit(self, mouse_pos: tuple[int, int]) -> bool:
|
|
791
|
+
"""Open the placed object's own properties under the cursor.
|
|
792
|
+
|
|
793
|
+
Returns True when an object was hit (event consumed), False otherwise
|
|
794
|
+
so the caller can fall back to the eyedropper behavior.
|
|
795
|
+
"""
|
|
796
|
+
active_layer = self.editor.tilemap.layer_manager.get_active_layer()
|
|
797
|
+
if not active_layer or active_layer.layer_type != "object":
|
|
798
|
+
return False
|
|
799
|
+
|
|
800
|
+
ts_widget = self.editor.tileset_widget
|
|
801
|
+
if not ts_widget:
|
|
802
|
+
return False
|
|
803
|
+
|
|
804
|
+
world_pos = self.screen_to_world(mouse_pos)
|
|
805
|
+
rs = self.editor.tilemap.render_scale
|
|
806
|
+
pick_pos = (int(world_pos[0] / rs), int(world_pos[1] / rs)) if rs else world_pos
|
|
807
|
+
|
|
808
|
+
for obj_id, obj in reversed(list(active_layer.get_all_objects().items())):
|
|
809
|
+
area = obj["area"]
|
|
810
|
+
obj_rect = Rect(area["x"], area["y"], area["w"], area["h"])
|
|
811
|
+
if obj_rect.collidepoint(pick_pos):
|
|
812
|
+
ts = ts_widget.get_tileset_by_index(int(obj["ttype"]))
|
|
813
|
+
self.editor.context_dispatch.open(
|
|
814
|
+
PropertyContext(
|
|
815
|
+
ContextKind.MAP_OBJECT,
|
|
816
|
+
obj,
|
|
817
|
+
{
|
|
818
|
+
"obj_id": obj_id,
|
|
819
|
+
"layer_name": active_layer.name,
|
|
820
|
+
"tileset_name": ts.name if ts else None,
|
|
821
|
+
},
|
|
822
|
+
)
|
|
823
|
+
)
|
|
824
|
+
return True
|
|
825
|
+
return False
|
|
826
|
+
|
|
789
827
|
def _point_in_selection(self, grid_pos: tuple[int, int]) -> bool:
|
|
790
828
|
"""Check if a grid position is inside the current selection."""
|
|
791
829
|
if not self.selection_rect:
|
|
@@ -4,6 +4,7 @@ from typing import TYPE_CHECKING, Any
|
|
|
4
4
|
import pygame
|
|
5
5
|
from pygame import Rect
|
|
6
6
|
|
|
7
|
+
from utils.context_dispatch import ContextKind, PropertyContext
|
|
7
8
|
from utils.error_handler import error_handler
|
|
8
9
|
from utils.validation import is_image_multipleof
|
|
9
10
|
from widgets.ui.button import Button
|
|
@@ -77,6 +78,17 @@ class TileSelector(WidgetBase):
|
|
|
77
78
|
self.zoom: float = 1.0
|
|
78
79
|
self.font = FONTS.get_medium_font()
|
|
79
80
|
|
|
81
|
+
self._register_context_handlers()
|
|
82
|
+
|
|
83
|
+
def _register_context_handlers(self):
|
|
84
|
+
d = self.editor.context_dispatch
|
|
85
|
+
d.register_opener(ContextKind.TILESET, self._open_tileset_properties)
|
|
86
|
+
d.register_saver(ContextKind.TILESET, self._save_tileset_properties)
|
|
87
|
+
d.register_opener(ContextKind.TILE_VARIANT, self._open_tile_variant_properties)
|
|
88
|
+
d.register_saver(ContextKind.TILE_VARIANT, self._save_tile_properties_multi)
|
|
89
|
+
d.register_opener(ContextKind.MAP_OBJECT, self._open_object_properties)
|
|
90
|
+
d.register_saver(ContextKind.MAP_OBJECT, self._save_object_properties)
|
|
91
|
+
|
|
80
92
|
def _add_folder(self) -> None:
|
|
81
93
|
self._folder_id_counter += 1
|
|
82
94
|
folder = TreeNode(
|
|
@@ -113,14 +125,52 @@ class TileSelector(WidgetBase):
|
|
|
113
125
|
ts = self._ts_node(item_id)
|
|
114
126
|
if ts is None:
|
|
115
127
|
return
|
|
128
|
+
self.editor.context_dispatch.open(PropertyContext(ContextKind.TILESET, ts))
|
|
129
|
+
|
|
130
|
+
def _open_tileset_properties(self, ctx: PropertyContext) -> None:
|
|
131
|
+
ts = ctx.target
|
|
116
132
|
self.editor.property_editor = PropertyEditor(
|
|
117
133
|
self.editor,
|
|
118
134
|
f"Tileset Properties: {ts.name}",
|
|
119
135
|
ts.properties,
|
|
120
|
-
|
|
121
|
-
|
|
136
|
+
context=ctx,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
def _open_tile_variant_properties(self, ctx: PropertyContext) -> None:
|
|
140
|
+
ts = ctx.target
|
|
141
|
+
variant_ids = ctx.extra.get("variant_ids", [])
|
|
142
|
+
if not variant_ids:
|
|
143
|
+
return
|
|
144
|
+
variant_id = variant_ids[0]
|
|
145
|
+
self.editor.property_editor = PropertyEditor(
|
|
146
|
+
self.editor,
|
|
147
|
+
f"Tile Properties: {ts.name} (ID: {variant_id})",
|
|
148
|
+
ts.tile_properties.get(variant_id, {}),
|
|
149
|
+
context=ctx,
|
|
122
150
|
)
|
|
123
151
|
|
|
152
|
+
def _open_object_properties(self, ctx: PropertyContext) -> None:
|
|
153
|
+
obj = ctx.target
|
|
154
|
+
if obj is None:
|
|
155
|
+
return
|
|
156
|
+
title = f"Object Properties: {ctx.extra.get('layer_name', '')} #{ctx.extra.get('obj_id', '?')}"
|
|
157
|
+
ts_name = ctx.extra.get("tileset_name")
|
|
158
|
+
if ts_name:
|
|
159
|
+
title = f"{title} ({ts_name})"
|
|
160
|
+
self.editor.property_editor = PropertyEditor(
|
|
161
|
+
self.editor,
|
|
162
|
+
title,
|
|
163
|
+
obj.get("properties", {}),
|
|
164
|
+
context=ctx,
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
def _save_object_properties(self, ctx: PropertyContext, props: dict) -> None:
|
|
168
|
+
obj = ctx.target
|
|
169
|
+
if obj is None:
|
|
170
|
+
return
|
|
171
|
+
obj["properties"] = props
|
|
172
|
+
self.editor.suggestion_registry.refresh(self.editor)
|
|
173
|
+
|
|
124
174
|
def _on_tree_structure_changed(self) -> None:
|
|
125
175
|
pass
|
|
126
176
|
|
|
@@ -221,11 +271,14 @@ class TileSelector(WidgetBase):
|
|
|
221
271
|
|
|
222
272
|
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 3:
|
|
223
273
|
if self.view_rect.collidepoint(mouse_pos) and self.active_idx != -1:
|
|
274
|
+
ts = self.tilesets[self.active_idx]
|
|
275
|
+
if ts.tileset_type == "object":
|
|
276
|
+
self.editor.context_dispatch.open(
|
|
277
|
+
PropertyContext(ContextKind.TILESET, ts)
|
|
278
|
+
)
|
|
279
|
+
return True
|
|
224
280
|
if self.selected_tile:
|
|
225
281
|
sx, sy, sw, sh = self.selected_tile
|
|
226
|
-
ts = self.tilesets[self.active_idx]
|
|
227
|
-
if ts.tileset_type == "object":
|
|
228
|
-
return True
|
|
229
282
|
img_x = self.view_rect.x + ts.offset[0]
|
|
230
283
|
img_y = self.view_rect.y + ts.offset[1]
|
|
231
284
|
sel_rect = Rect(img_x + sx, img_y + sy, sw, sh)
|
|
@@ -235,20 +288,18 @@ class TileSelector(WidgetBase):
|
|
|
235
288
|
if not variant_ids:
|
|
236
289
|
return True
|
|
237
290
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
on_save=lambda props: self._save_tile_properties_multi(ts, variant_ids, props),
|
|
245
|
-
on_close=lambda: None,
|
|
291
|
+
self.editor.context_dispatch.open(
|
|
292
|
+
PropertyContext(
|
|
293
|
+
ContextKind.TILE_VARIANT,
|
|
294
|
+
ts,
|
|
295
|
+
{"variant_ids": variant_ids},
|
|
296
|
+
)
|
|
246
297
|
)
|
|
247
298
|
return True
|
|
248
299
|
|
|
249
300
|
self.is_panning = True
|
|
250
301
|
self.pan_start = mouse_pos
|
|
251
|
-
self.pan_start_offset = tuple(
|
|
302
|
+
self.pan_start_offset = tuple(ts.offset)
|
|
252
303
|
return True
|
|
253
304
|
|
|
254
305
|
elif event.type == pygame.MOUSEBUTTONUP and event.button == 3:
|
|
@@ -289,9 +340,19 @@ class TileSelector(WidgetBase):
|
|
|
289
340
|
ts = self.tilesets[self.active_idx]
|
|
290
341
|
img_x = self.view_rect.x + ts.offset[0]
|
|
291
342
|
img_y = self.view_rect.y + ts.offset[1]
|
|
292
|
-
|
|
293
343
|
rel_x = (mouse_pos[0] - img_x) / self.zoom
|
|
294
344
|
rel_y = (mouse_pos[1] - img_y) / self.zoom
|
|
345
|
+
if ts.tileset_type == "object":
|
|
346
|
+
if (
|
|
347
|
+
0 <= rel_x < ts.surface.get_width()
|
|
348
|
+
and 0 <= rel_y < ts.surface.get_height()
|
|
349
|
+
):
|
|
350
|
+
self.hover_pos = (0, 0)
|
|
351
|
+
else:
|
|
352
|
+
self.hover_pos = None
|
|
353
|
+
self.is_selecting = False
|
|
354
|
+
self.selection_start_grid = None
|
|
355
|
+
return True
|
|
295
356
|
|
|
296
357
|
if 0 <= rel_x < ts.surface.get_width() and 0 <= rel_y < ts.surface.get_height():
|
|
297
358
|
tw, th = self.editor.tilemap.tile_size
|
|
@@ -574,7 +635,8 @@ class TileSelector(WidgetBase):
|
|
|
574
635
|
|
|
575
636
|
self.editor.launch_collision_editor(ts.tileset_type)
|
|
576
637
|
|
|
577
|
-
def _save_tileset_properties(self,
|
|
638
|
+
def _save_tileset_properties(self, ctx: PropertyContext, props: dict):
|
|
639
|
+
ts = ctx.target
|
|
578
640
|
ts.properties = props
|
|
579
641
|
self.editor.suggestion_registry.refresh(self.editor)
|
|
580
642
|
print(f"Saved properties for tileset: {ts.name}")
|
|
@@ -583,7 +645,9 @@ class TileSelector(WidgetBase):
|
|
|
583
645
|
ts.tile_properties[variant_id] = props
|
|
584
646
|
print(f"Saved properties for tile {variant_id} in tileset: {ts.name}")
|
|
585
647
|
|
|
586
|
-
def _save_tile_properties_multi(self,
|
|
648
|
+
def _save_tile_properties_multi(self, ctx: PropertyContext, props: dict):
|
|
649
|
+
ts = ctx.target
|
|
650
|
+
variant_ids = ctx.extra.get("variant_ids", [])
|
|
587
651
|
for vid in variant_ids:
|
|
588
652
|
ts.tile_properties[vid] = props.copy()
|
|
589
653
|
self.editor.suggestion_registry.refresh(self.editor)
|
|
@@ -613,6 +677,11 @@ class TileSelector(WidgetBase):
|
|
|
613
677
|
return None
|
|
614
678
|
return self.tilesets[self.active_idx]
|
|
615
679
|
|
|
680
|
+
def get_tileset_by_index(self, index: int) -> TilesetData | None:
|
|
681
|
+
if index < 0 or index >= len(self.tilesets):
|
|
682
|
+
return None
|
|
683
|
+
return self.tilesets[index]
|
|
684
|
+
|
|
616
685
|
def select_tile_by_variant(self, tileset_index: int, variant_id: int) -> bool:
|
|
617
686
|
if tileset_index < 0 or tileset_index >= len(self.tilesets):
|
|
618
687
|
return False
|
|
@@ -802,6 +871,8 @@ class TileSelector(WidgetBase):
|
|
|
802
871
|
def draw_hover(self, screen, ts: TilesetData, img_x: int, img_y: int):
|
|
803
872
|
if self.hover_pos is None:
|
|
804
873
|
return
|
|
874
|
+
if ts.tileset_type == "object":
|
|
875
|
+
return
|
|
805
876
|
tw, th = self.editor.tilemap.tile_size
|
|
806
877
|
col, row = self.hover_pos
|
|
807
878
|
ztw = int(tw * self.zoom)
|
|
@@ -20,6 +20,7 @@ class Button(WidgetBase):
|
|
|
20
20
|
font=None,
|
|
21
21
|
text_color=None,
|
|
22
22
|
accent=False,
|
|
23
|
+
danger=False,
|
|
23
24
|
bg=None,
|
|
24
25
|
border_color=None,
|
|
25
26
|
border_radius=None,
|
|
@@ -43,6 +44,7 @@ class Button(WidgetBase):
|
|
|
43
44
|
self.font = font or FONTS.get_medium_font()
|
|
44
45
|
self._text_color = text_color
|
|
45
46
|
self.accent = accent
|
|
47
|
+
self.danger = danger
|
|
46
48
|
self.on_click = on_click
|
|
47
49
|
self._active = False
|
|
48
50
|
self._hovered = False
|
|
@@ -92,18 +94,30 @@ class Button(WidgetBase):
|
|
|
92
94
|
border = COLORS.border
|
|
93
95
|
text_color = self._text_color or COLORS.text
|
|
94
96
|
elif self._pressed:
|
|
95
|
-
bg =
|
|
97
|
+
bg = (
|
|
98
|
+
COLORS.accent_active
|
|
99
|
+
if self.accent
|
|
100
|
+
else (COLORS.danger_hover if self.danger else COLORS.header)
|
|
101
|
+
)
|
|
96
102
|
border = COLORS.border
|
|
97
103
|
text_color = self._text_color or COLORS.text
|
|
98
104
|
elif self._hovered:
|
|
99
|
-
bg =
|
|
105
|
+
bg = (
|
|
106
|
+
COLORS.accent_hover
|
|
107
|
+
if self.accent
|
|
108
|
+
else (COLORS.danger_hover if self.danger else COLORS.hover)
|
|
109
|
+
)
|
|
100
110
|
border = COLORS.border
|
|
101
111
|
text_color = self._text_color or COLORS.text
|
|
102
112
|
else:
|
|
103
113
|
bg = (
|
|
104
114
|
self._bg
|
|
105
115
|
if self._bg is not None
|
|
106
|
-
else (
|
|
116
|
+
else (
|
|
117
|
+
COLORS.accent
|
|
118
|
+
if self.accent
|
|
119
|
+
else COLORS.danger if self.danger else COLORS.panel_alt
|
|
120
|
+
)
|
|
107
121
|
)
|
|
108
122
|
border = self._border if self._border is not None else COLORS.border_soft
|
|
109
123
|
text_color = self._text_color or COLORS.text
|
|
@@ -5,6 +5,7 @@ from pygame import Rect
|
|
|
5
5
|
|
|
6
6
|
if TYPE_CHECKING:
|
|
7
7
|
from editor import Editor
|
|
8
|
+
from utils.context_dispatch import ContextKind, PropertyContext
|
|
8
9
|
from utils.font_manager import FontWeight, font_manager
|
|
9
10
|
from widgets.particle_presets import PRESETS, get_preset_config, get_preset_names
|
|
10
11
|
from widgets.ui.draw_utils import draw_panel
|
|
@@ -37,6 +38,10 @@ class NodeEditor:
|
|
|
37
38
|
self._preset_names: list[str] = get_preset_names()
|
|
38
39
|
self._is_particle = False
|
|
39
40
|
|
|
41
|
+
d = self.editor.context_dispatch
|
|
42
|
+
d.register_opener(ContextKind.NODE, self._open_node_properties)
|
|
43
|
+
d.register_saver(ContextKind.NODE, self._save_props)
|
|
44
|
+
|
|
40
45
|
def resize(self, x: int, y: int, w: int):
|
|
41
46
|
self.rect = Rect(x, y, w, self.rect.height)
|
|
42
47
|
|
|
@@ -215,12 +220,8 @@ class NodeEditor:
|
|
|
215
220
|
if action == "props":
|
|
216
221
|
node = mgr.get_active_node()
|
|
217
222
|
if node:
|
|
218
|
-
self.editor.
|
|
219
|
-
|
|
220
|
-
f"Node Properties: {node.name}",
|
|
221
|
-
dict(node.properties),
|
|
222
|
-
on_save=lambda props: self._save_props(props),
|
|
223
|
-
on_close=lambda: None,
|
|
223
|
+
self.editor.context_dispatch.open(
|
|
224
|
+
PropertyContext(ContextKind.NODE, node)
|
|
224
225
|
)
|
|
225
226
|
elif action == "particle":
|
|
226
227
|
node = mgr.get_active_node()
|
|
@@ -352,8 +353,19 @@ class NodeEditor:
|
|
|
352
353
|
except (ValueError, TypeError):
|
|
353
354
|
return default
|
|
354
355
|
|
|
355
|
-
def
|
|
356
|
-
node =
|
|
356
|
+
def _open_node_properties(self, ctx: PropertyContext) -> None:
|
|
357
|
+
node = ctx.target
|
|
358
|
+
if node is None:
|
|
359
|
+
return
|
|
360
|
+
self.editor.property_editor = PropertyEditor(
|
|
361
|
+
self.editor,
|
|
362
|
+
f"Node Properties: {node.name}",
|
|
363
|
+
dict(node.properties),
|
|
364
|
+
context=ctx,
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
def _save_props(self, ctx: PropertyContext, props: dict):
|
|
368
|
+
node = ctx.target
|
|
357
369
|
if node:
|
|
358
370
|
node.properties = props
|
|
359
371
|
self.editor.suggestion_registry.refresh(self.editor)
|