tilemap-editor 4.3.8__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.8 → tilemap_editor-4.4.0}/CHANGELOG.md +19 -1
- {tilemap_editor-4.3.8/src/tilemap_editor.egg-info → tilemap_editor-4.4.0}/PKG-INFO +1 -1
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/pyproject.toml +1 -1
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/editor.py +2 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/tileset_collision/editor.py +12 -5
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0/src/tilemap_editor.egg-info}/PKG-INFO +1 -1
- {tilemap_editor-4.3.8 → 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.8 → tilemap_editor-4.4.0}/src/utils/font_manager.py +15 -1
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/layer_selector.py +19 -11
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/tile_grid.py +40 -2
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/tile_selector.py +88 -17
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/ui/button.py +17 -3
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/ui/node_editor.py +20 -8
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/ui/property_editor.py +76 -20
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/ui/region_selector.py +20 -6
- 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.8 → tilemap_editor-4.4.0}/tests/test_tile_selector_pick.py +41 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/LICENSE +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/MANIFEST.in +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/README.md +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/arrow-down.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/check.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/checked.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/close.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/duplicate.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/error.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/file.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/filedead.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/fit.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/folder.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/grid.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/image.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/info.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/load.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/loop.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/onion.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/pan.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/pause.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/pencil.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/play.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/plus.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/radio.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/reset.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/save.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/skip-back.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/skip-forward.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/stop.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/tilemap.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/tileset.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/unchecked.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/warning.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/zoomin.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/assets/icons/zoomout.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/setup.cfg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/constants.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/event_map.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/layers.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/main.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/node_manager.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/nodes.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/__init__.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/character_collision/__init__.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/character_collision/editor.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/character_collision/models.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/character_collision/protocols.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/character_collision/shape_editor.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/character_collision/standalone.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/object_tileset_collision/__init__.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/object_tileset_collision/editor.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/object_tileset_collision/models.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/object_tileset_collision/protocols.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/object_tileset_collision/standalone.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/sprite_animation/__init__.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/sprite_animation/__main__.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/sprite_animation/clipboard_util.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/sprite_animation/editor.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/sprite_animation/frame_picker.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/sprite_animation/models.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/sprite_animation/preview.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/sprite_animation/protocols.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/sprite_animation/runtime_load.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/sprite_animation/standalone.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/sprite_animation/timeline.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/sprite_animation/validation.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/sprite_editor/__init__.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/sprite_editor/camera.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/sprite_editor/clipboard.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/sprite_editor/commands.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/sprite_editor/dialogs.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/sprite_editor/document.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/sprite_editor/editor.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/sprite_editor/overlays.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/sprite_editor/region_export.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/sprite_editor/selection.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/sprite_editor/standalone.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/sprite_editor/tools.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/sprite_editor/viewport.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/tileset_collision/__init__.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/tileset_collision/collision_painter.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/tileset_collision/models.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/tileset_collision/protocols.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/plugins/tileset_collision/standalone.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/run.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/standalone_automap.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/standalone_error_console.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/standalone_filemanager.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/standalone_image_viewer.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/__init__.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/__main__.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/__init__.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/ToolMove.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/arrow-down.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/check.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/checked.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/close.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/duplicate.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/error.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/file.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/filedead.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/fit.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/fold_down_arrow.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/folder.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/gear.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/grid.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/image.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/info.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/load.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/loop.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/miniobj.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/onion.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/pan.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/pause.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/pencil.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/play.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/plus.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/radio.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/reset.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/save.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/skip-back.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/skip-forward.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/stop.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/tilemap.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/tileset.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/unchecked.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/unfold_right_arrow.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/warning.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/zoomin.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/assets/icons/zoomout.svg +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/cli.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/main.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor/settings.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor.egg-info/dependency_links.txt +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor.egg-info/entry_points.txt +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor.egg-info/requires.txt +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/tilemap_editor.egg-info/top_level.txt +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/ttypes/__init__.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/ttypes/tilemap.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/utils/__init__.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/utils/editor_preference.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/utils/error_handler.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/utils/history.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/utils/icon_manager.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/utils/icons_cache.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/utils/log_capture.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/utils/natural_sort.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/utils/project_paths.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/utils/property_suggestions.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/utils/serialization.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/utils/standalone.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/utils/validation.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/__init__.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/automap_models.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/autotile_template.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/autotiler.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/filemanager.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/input.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/map_properties.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/mapsetup.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/particle_presets.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/particle_system.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/regex_automap_designer.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/ui/checkbox.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/ui/collision_layer_mask.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/ui/collision_layer_sidebar.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/ui/confirm_dialog.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/ui/dialog_base.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/ui/drag_tracker.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/ui/draw_utils.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/ui/fileinput.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/ui/label.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/ui/layer_type_dialog.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/ui/menubar.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/ui/mode_indicator.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/ui/node_selector.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/ui/notification.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/ui/particle_config_dialog.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/ui/resize_map_dialog.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/ui/scrollbar.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/ui/sidebar_container.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/ui/splitter.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/ui/status_bar.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/ui/theme.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/ui/tileset_type_dialog.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/ui/toast.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/ui/tool_manager.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/ui/toolbar.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/ui/tooltip.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/ui/tree_widget.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/src/widgets/widget_base.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/tests/test_animation_library_grid_offset.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/tests/test_collision_layer_mask.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/tests/test_collision_layer_sidebar.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/tests/test_drag_tracker.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/tests/test_editor_pan_mode.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/tests/test_gid_collision.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/tests/test_layers_autotile.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/tests/test_map_resize_model.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/tests/test_natural_sort.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/tests/test_project_paths.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/tests/test_render_scale.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/tests/test_serialization_regression.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/tests/test_sprite_animation_editor_grid.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/tests/test_sprite_editor_commands.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/tests/test_sprite_editor_interaction.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/tests/test_sprite_editor_models.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/tests/test_tile_grid_selection.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/tests/test_tilemap_save.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/tests/test_timeline_invalidate_cache.py +0 -0
- {tilemap_editor-4.3.8 → tilemap_editor-4.4.0}/tests/test_toolbar_tools.py +0 -0
|
@@ -1,15 +1,33 @@
|
|
|
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
|
+
|
|
14
|
+
# tilemap_editor 4.3.9 (2026-08-04)
|
|
15
|
+
|
|
16
|
+
## Bug Fixes
|
|
17
|
+
|
|
18
|
+
- Fixed tileset collision editor tile picking when the tileset is centered in the selector: mouse clicks now map to the correct tile after the centering offset is applied.
|
|
19
|
+
- Fixed region selector coordinate conversion (screen↔image) to account for the centered image when it is smaller than the viewport, so drawing, selection, and drag operations stay aligned.
|
|
20
|
+
|
|
4
21
|
# tilemap_editor 4.3.8 (2026-08-04)
|
|
5
22
|
|
|
6
23
|
## Features
|
|
7
24
|
|
|
8
|
-
- Sprite editor: two-row grouped toolbar
|
|
25
|
+
- Sprite editor: two-row grouped toolbar, natural filename sorting on multi-sheet loads, and a Stack V/H toggle for vertical or horizontal sheet stacking.
|
|
9
26
|
- Rewrote the sprite editor around a command stack with single-canvas undo memory: shared clipboard with relative-tile pasting that survives scale/grid changes, drag-move and rubber-band selection that can expand the canvas, object-selection undo restore, and a pure camera with cursor-anchored zoom. (sprite-editor-v2)
|
|
10
27
|
|
|
11
28
|
## Bug Fixes
|
|
12
29
|
|
|
30
|
+
- Fixed sprite editor undo history corruption from shared region references, viewport cache staleness after region edits, coordinate errors after loading new sheets, index lookup failures with negative-origin canvases, and canvas expansion failures for negative cell coordinates
|
|
13
31
|
- Fixed sprite editor paste landing in wrong cells after a scale or grid-size change (clipboard now stores relative tile offsets), paste/cut double-clipboard split-brain, paste preview lacking pixel ghost, and moves/pastes past the canvas edge now expanding the canvas. (sprite-editor-paste-fixes)
|
|
14
32
|
- Fixed sprite editor theming and camera gestures: the canvas, header bar, grid lines, and overlays now use theme colors/fonts instead of hardcoded dark values (canvas no longer stays black on light themes), the canvas header no longer covers the sheet's top rows, and mouse wheel zoom/pan work again with the same 30px/1.12x behavior as the old editor. (sprite-editor-theme-wheel)
|
|
15
33
|
|
|
@@ -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
|
|
@@ -59,7 +59,6 @@ class TilesetCollisionEditor:
|
|
|
59
59
|
self.rect = rect
|
|
60
60
|
self.consumer = consumer
|
|
61
61
|
self.visible = True
|
|
62
|
-
|
|
63
62
|
if provider is not None:
|
|
64
63
|
self._provider = provider
|
|
65
64
|
self._tileset_surface = provider.get_surface()
|
|
@@ -791,17 +790,25 @@ class TilesetCollisionEditor:
|
|
|
791
790
|
if not self.tileset_selector_rect.collidepoint(mouse):
|
|
792
791
|
return None
|
|
793
792
|
|
|
794
|
-
rel_x = mouse[0] - self.tileset_selector_rect.x + self.tileset_scroll_x
|
|
795
|
-
rel_y = mouse[1] - self.tileset_selector_rect.y + self.tileset_scroll_y
|
|
796
|
-
|
|
797
793
|
tw = int(self._tile_size[0] * self.tileset_zoom)
|
|
798
794
|
th = int(self._tile_size[1] * self.tileset_zoom)
|
|
799
795
|
|
|
796
|
+
# Calculate centering offset (same as in _draw_tileset_selector)
|
|
797
|
+
tileset_scr_w = self.tile_cols * tw
|
|
798
|
+
tileset_scr_h = self.tile_rows * th
|
|
799
|
+
center_off_x = max(0, (self.tileset_selector_rect.w - tileset_scr_w) // 2)
|
|
800
|
+
center_off_y = max(0, (self.tileset_selector_rect.h - tileset_scr_h) // 2)
|
|
801
|
+
|
|
802
|
+
# Apply centering offset to mouse calculation
|
|
803
|
+
rel_x = mouse[0] - self.tileset_selector_rect.x + self.tileset_scroll_x - center_off_x
|
|
804
|
+
rel_y = mouse[1] - self.tileset_selector_rect.y + self.tileset_scroll_y - center_off_y
|
|
805
|
+
|
|
800
806
|
col = rel_x // tw
|
|
801
807
|
row = rel_y // th
|
|
802
808
|
|
|
803
809
|
if 0 <= col < self.tile_cols and 0 <= row < self.tile_rows:
|
|
804
|
-
|
|
810
|
+
tile_id = row * self.tile_cols + col
|
|
811
|
+
return tile_id
|
|
805
812
|
|
|
806
813
|
return None
|
|
807
814
|
|
|
@@ -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
|
|
@@ -50,7 +50,7 @@ class FontManager:
|
|
|
50
50
|
self._fonts: dict[str, pygame.font.Font] = {}
|
|
51
51
|
self._font_families: dict[str, dict[str, str]] = {}
|
|
52
52
|
self._default_family = "Arial"
|
|
53
|
-
self._assets_path =
|
|
53
|
+
self._assets_path = self._resolve_assets_path()
|
|
54
54
|
logger.info(f"Font Manager initializing - Assets path: {self._assets_path}")
|
|
55
55
|
self._load_font_families()
|
|
56
56
|
logger.info(
|
|
@@ -58,6 +58,20 @@ class FontManager:
|
|
|
58
58
|
)
|
|
59
59
|
FontManager._initialized = True
|
|
60
60
|
|
|
61
|
+
@staticmethod
|
|
62
|
+
def _resolve_assets_path() -> Path:
|
|
63
|
+
"""Find bundled fonts in package data, falling back to the dev layout."""
|
|
64
|
+
try:
|
|
65
|
+
from importlib.resources import files
|
|
66
|
+
|
|
67
|
+
pkg_path = files("tilemap_editor.assets") / "fonts"
|
|
68
|
+
path = Path(str(pkg_path))
|
|
69
|
+
if path.exists() and any(path.iterdir()):
|
|
70
|
+
return path
|
|
71
|
+
except (ImportError, TypeError, OSError):
|
|
72
|
+
pass
|
|
73
|
+
return Path(__file__).parent.parent.parent / "assets" / "fonts"
|
|
74
|
+
|
|
61
75
|
def _load_font_families(self):
|
|
62
76
|
"""Load available font families from assets directory."""
|
|
63
77
|
if not self._assets_path.exists():
|
|
@@ -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
|