tilemap-editor 3.0.1__tar.gz → 3.1.2__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.
Files changed (179) hide show
  1. {tilemap_editor-3.0.1/src/tilemap_editor.egg-info → tilemap_editor-3.1.2}/PKG-INFO +1 -1
  2. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/pyproject.toml +1 -2
  3. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/character_collision/editor.py +41 -1
  4. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/character_collision/shape_editor.py +186 -7
  5. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/object_tileset_collision/editor.py +45 -0
  6. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap.py +11 -7
  7. tilemap_editor-3.1.2/src/tilemap_editor/assets/icons/ToolMove.svg +1 -0
  8. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2/src/tilemap_editor.egg-info}/PKG-INFO +1 -1
  9. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor.egg-info/SOURCES.txt +7 -1
  10. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor.egg-info/top_level.txt +0 -1
  11. tilemap_editor-3.1.2/src/widgets/ui/collision_layer_mask.py +217 -0
  12. tilemap_editor-3.1.2/src/widgets/ui/collision_layer_sidebar.py +214 -0
  13. tilemap_editor-3.1.2/tests/test_collision_layer_mask.py +226 -0
  14. tilemap_editor-3.1.2/tests/test_collision_layer_sidebar.py +177 -0
  15. tilemap_editor-3.1.2/tests/test_project_paths.py +73 -0
  16. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/LICENSE +0 -0
  17. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/MANIFEST.in +0 -0
  18. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/README.md +0 -0
  19. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
  20. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
  21. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
  22. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
  23. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
  24. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
  25. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
  26. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
  27. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/arrow-down.svg +0 -0
  28. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/check.svg +0 -0
  29. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/checked.svg +0 -0
  30. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/close.svg +0 -0
  31. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/duplicate.svg +0 -0
  32. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/error.svg +0 -0
  33. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/file.svg +0 -0
  34. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/filedead.svg +0 -0
  35. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/fit.svg +0 -0
  36. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/folder.svg +0 -0
  37. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/grid.svg +0 -0
  38. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/image.svg +0 -0
  39. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/info.svg +0 -0
  40. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/load.svg +0 -0
  41. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/loop.svg +0 -0
  42. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/pan.svg +0 -0
  43. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/pause.svg +0 -0
  44. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/pencil.svg +0 -0
  45. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/play.svg +0 -0
  46. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/plus.svg +0 -0
  47. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/radio.svg +0 -0
  48. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/reset.svg +0 -0
  49. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/save.svg +0 -0
  50. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/stop.svg +0 -0
  51. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/tilemap.svg +0 -0
  52. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/tileset.svg +0 -0
  53. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/unchecked.svg +0 -0
  54. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/warning.svg +0 -0
  55. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/zoomin.svg +0 -0
  56. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/zoomout.svg +0 -0
  57. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/setup.cfg +0 -0
  58. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/configs/themes.py +0 -0
  59. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/constants.py +0 -0
  60. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/editor.py +0 -0
  61. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/event_map.py +0 -0
  62. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/layers.py +0 -0
  63. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/main.py +0 -0
  64. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/__init__.py +0 -0
  65. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/character_collision/__init__.py +0 -0
  66. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/character_collision/models.py +0 -0
  67. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/character_collision/protocols.py +0 -0
  68. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/character_collision/standalone.py +0 -0
  69. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/object_tileset_collision/__init__.py +0 -0
  70. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/object_tileset_collision/models.py +0 -0
  71. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/object_tileset_collision/protocols.py +0 -0
  72. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/object_tileset_collision/standalone.py +0 -0
  73. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/sprite_animation/__init__.py +0 -0
  74. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/sprite_animation/__main__.py +0 -0
  75. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/sprite_animation/clipboard_util.py +0 -0
  76. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/sprite_animation/editor.py +0 -0
  77. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/sprite_animation/frame_picker.py +0 -0
  78. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/sprite_animation/models.py +0 -0
  79. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/sprite_animation/preview.py +0 -0
  80. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/sprite_animation/protocols.py +0 -0
  81. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/sprite_animation/runtime_load.py +0 -0
  82. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/sprite_animation/standalone.py +0 -0
  83. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/sprite_animation/timeline.py +0 -0
  84. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/sprite_animation/validation.py +0 -0
  85. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/tileset_collision/__init__.py +0 -0
  86. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/tileset_collision/collision_painter.py +0 -0
  87. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/tileset_collision/editor.py +0 -0
  88. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/tileset_collision/models.py +0 -0
  89. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/tileset_collision/protocols.py +0 -0
  90. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/tileset_collision/standalone.py +0 -0
  91. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/standalone_automap.py +0 -0
  92. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/standalone_error_console.py +0 -0
  93. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/standalone_filemanager.py +0 -0
  94. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/standalone_image_viewer.py +0 -0
  95. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/__init__.py +0 -0
  96. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/__main__.py +0 -0
  97. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/__init__.py +0 -0
  98. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
  99. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
  100. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
  101. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
  102. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
  103. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
  104. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
  105. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
  106. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/arrow-down.svg +0 -0
  107. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/check.svg +0 -0
  108. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/checked.svg +0 -0
  109. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/close.svg +0 -0
  110. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/duplicate.svg +0 -0
  111. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/error.svg +0 -0
  112. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/file.svg +0 -0
  113. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/filedead.svg +0 -0
  114. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/fit.svg +0 -0
  115. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/folder.svg +0 -0
  116. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/grid.svg +0 -0
  117. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/image.svg +0 -0
  118. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/info.svg +0 -0
  119. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/load.svg +0 -0
  120. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/loop.svg +0 -0
  121. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/pan.svg +0 -0
  122. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/pause.svg +0 -0
  123. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/pencil.svg +0 -0
  124. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/play.svg +0 -0
  125. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/plus.svg +0 -0
  126. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/radio.svg +0 -0
  127. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/reset.svg +0 -0
  128. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/save.svg +0 -0
  129. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/stop.svg +0 -0
  130. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/tilemap.svg +0 -0
  131. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/tileset.svg +0 -0
  132. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/unchecked.svg +0 -0
  133. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/warning.svg +0 -0
  134. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/zoomin.svg +0 -0
  135. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/zoomout.svg +0 -0
  136. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/cli.py +0 -0
  137. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/main.py +0 -0
  138. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/settings.py +0 -0
  139. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor.egg-info/dependency_links.txt +0 -0
  140. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor.egg-info/entry_points.txt +0 -0
  141. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor.egg-info/requires.txt +0 -0
  142. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/ttypes/__init__.py +0 -0
  143. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/ttypes/tilemap.py +0 -0
  144. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/utils/__init__.py +0 -0
  145. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/utils/editor_preference.py +0 -0
  146. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/utils/error_handler.py +0 -0
  147. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/utils/font_manager.py +0 -0
  148. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/utils/history.py +0 -0
  149. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/utils/icon_manager.py +0 -0
  150. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/utils/icons_cache.py +0 -0
  151. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/utils/log_capture.py +0 -0
  152. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/utils/project_paths.py +0 -0
  153. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/utils/serialization.py +0 -0
  154. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/utils/standalone.py +0 -0
  155. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/utils/validation.py +0 -0
  156. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/__init__.py +0 -0
  157. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/automap_models.py +0 -0
  158. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/autotile_template.py +0 -0
  159. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/autotiler.py +0 -0
  160. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/filemanager.py +0 -0
  161. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/input.py +0 -0
  162. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/layer_selector.py +0 -0
  163. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/mapsetup.py +0 -0
  164. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/regex_automap_designer.py +0 -0
  165. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/tile_grid.py +0 -0
  166. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/tile_selector.py +0 -0
  167. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/ui/draw_utils.py +0 -0
  168. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/ui/fileinput.py +0 -0
  169. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/ui/layer_type_dialog.py +0 -0
  170. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/ui/menubar.py +0 -0
  171. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/ui/mode_indicator.py +0 -0
  172. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/ui/notification.py +0 -0
  173. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/ui/property_editor.py +0 -0
  174. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/ui/region_selector.py +0 -0
  175. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/ui/status_bar.py +0 -0
  176. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/ui/theme.py +0 -0
  177. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/ui/tileset_type_dialog.py +0 -0
  178. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/ui/toolbar.py +0 -0
  179. {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/ui/tooltip.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tilemap-editor
3
- Version: 3.0.1
3
+ Version: 3.1.2
4
4
  Summary: Pygame tilemap editor with SVG icons, professional UI, and sprite animation tools
5
5
  Author: tilemap editor contributors
6
6
  License: Proprietary
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tilemap-editor"
7
- version = "3.0.1"
7
+ version = "3.1.2"
8
8
  description = "Pygame tilemap editor with SVG icons, professional UI, and sprite animation tools"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -33,7 +33,6 @@ py-modules = [
33
33
  "event_map",
34
34
  "layers",
35
35
  "tilemap",
36
- "tilemap_parser",
37
36
  "standalone_filemanager",
38
37
  "standalone_image_viewer",
39
38
  "standalone_automap",
@@ -29,6 +29,7 @@ from utils.icon_manager import icon_manager
29
29
  from utils.error_handler import error_handler, error_context
30
30
  from widgets.ui.theme import COLORS, FONTS
31
31
  from widgets.ui.draw_utils import draw_panel, draw_button
32
+ from widgets.ui.collision_layer_sidebar import CollisionLayerSidebar
32
33
 
33
34
 
34
35
  class CharacterCollisionEditor:
@@ -60,6 +61,14 @@ class CharacterCollisionEditor:
60
61
  # Shape editor
61
62
  self.shape_editor = ShapeEditor(shape_editor_rect, sprite_surface)
62
63
 
64
+ # Collision layer/mask sidebar (toggleable overlay)
65
+ self.layer_sidebar = CollisionLayerSidebar(
66
+ rect,
67
+ max_layers=16,
68
+ initial_layer=1,
69
+ initial_mask=0xFFFF,
70
+ )
71
+
63
72
  # Fonts
64
73
  self._font = font_manager.get_font(FONTS.name, FONTS.size_md, FontWeight.REGULAR)
65
74
  self._font_sm = font_manager.get_font(FONTS.name, FONTS.size_sm, FontWeight.REGULAR)
@@ -93,6 +102,8 @@ class CharacterCollisionEditor:
93
102
  )
94
103
 
95
104
  self.shape_editor.resize(shape_editor_rect)
105
+ self.layer_sidebar.resize(rect)
106
+
96
107
  self._init_shape_buttons()
97
108
 
98
109
  def get_collision_data(self) -> CharacterCollisionData:
@@ -136,12 +147,20 @@ class CharacterCollisionEditor:
136
147
  return CharacterCollisionData(
137
148
  name=self.character_name,
138
149
  shape=shape,
150
+ properties={
151
+ "collision_layer": self.layer_sidebar.get_layer(),
152
+ "collision_mask": self.layer_sidebar.get_mask(),
153
+ },
139
154
  )
140
155
 
141
156
  def load_collision_data(self, data: CharacterCollisionData) -> None:
142
157
  """Load collision data"""
143
158
  self.character_name = data.name
144
159
  self.shape_editor.load_shape_data(data.shape.to_dict())
160
+ layer = data.properties.get("collision_layer", 1)
161
+ mask = data.properties.get("collision_mask", 0xFFFF)
162
+ self.layer_sidebar.set_layer(layer)
163
+ self.layer_sidebar.set_mask(mask)
145
164
 
146
165
  def _get_collision_dir(self) -> Path:
147
166
  """Get collision data directory (data_root/character_collision)"""
@@ -175,7 +194,22 @@ class CharacterCollisionEditor:
175
194
  if not self.visible:
176
195
  return False
177
196
 
178
- # Let shape editor handle events first
197
+ # Layer/mask sidebar handles events when open
198
+ if self.layer_sidebar.handle_event(event):
199
+ return True
200
+
201
+ # Toggle button
202
+ if self.layer_sidebar.handle_toggle_event(event):
203
+ return True
204
+
205
+ # Keyboard shortcut: L to toggle sidebar
206
+ if event.type == pygame.KEYDOWN and event.key == pygame.K_l:
207
+ mods = pygame.key.get_mods()
208
+ if not (mods & (pygame.KMOD_CTRL | pygame.KMOD_LMETA)):
209
+ self.layer_sidebar.toggle()
210
+ return True
211
+
212
+ # Let shape editor handle events next
179
213
  if self.shape_editor.handle_event(event):
180
214
  return True
181
215
 
@@ -201,6 +235,12 @@ class CharacterCollisionEditor:
201
235
  # Draw shape editor
202
236
  self.shape_editor.draw(screen)
203
237
 
238
+ # Draw toggle button
239
+ self.layer_sidebar.draw_toggle_button(screen)
240
+
241
+ # Draw collision layer/mask sidebar (only when visible)
242
+ self.layer_sidebar.draw(screen)
243
+
204
244
  # Draw properties panel
205
245
  self._draw_properties(screen)
206
246
 
@@ -18,6 +18,7 @@ import pygame
18
18
  from pygame import Rect
19
19
  from utils.font_manager import font_manager, FontWeight
20
20
  from utils.error_handler import error_handler
21
+ from utils.icon_manager import icon_manager
21
22
 
22
23
 
23
24
  ShapeType = Literal["rectangle", "circle", "capsule", "polygon"]
@@ -82,8 +83,14 @@ class ShapeEditor:
82
83
  self.dragging_handle: Optional[str] = None
83
84
  self.hover_handle: Optional[str] = None
84
85
  self._panning = False
86
+ self._pan_mode = False
85
87
  self._pan_start = (0, 0)
86
88
  self._pan_start_offset = (0.0, 0.0)
89
+ self._move_icon_pos: Tuple[int, int] = (0, 0)
90
+ self._move_icon_hover = False
91
+ self._dragging_shape = False
92
+ self._drag_start = (0, 0)
93
+ self._drag_start_positions: dict = {}
87
94
 
88
95
  # Settings
89
96
  self.show_grid = True
@@ -95,6 +102,7 @@ class ShapeEditor:
95
102
 
96
103
  # Center the sprite
97
104
  self._center_view()
105
+ self._center_shape()
98
106
 
99
107
  def _center_view(self) -> None:
100
108
  """Center the sprite in the viewport"""
@@ -102,6 +110,27 @@ class ShapeEditor:
102
110
  self.offset_x = (self.rect.w - sw * self.zoom) / 2
103
111
  self.offset_y = (self.rect.h - sh * self.zoom) / 2
104
112
 
113
+ def _center_shape(self) -> None:
114
+ """Center all shape types on the sprite"""
115
+ sw, sh = self.sprite_surface.get_size()
116
+
117
+ # Rectangle: 50% of sprite, centered
118
+ self.rect_w = sw * 0.5
119
+ self.rect_h = sh * 0.5
120
+ self.rect_x = (sw - self.rect_w) / 2
121
+ self.rect_y = (sh - self.rect_h) / 2
122
+
123
+ # Circle: 25% of min dimension, centered
124
+ self.circle_radius = min(sw, sh) * 0.25
125
+ self.circle_x = sw / 2
126
+ self.circle_y = sh / 2
127
+
128
+ # Capsule: 50% height, 15% width radius, centered
129
+ self.capsule_height = sh * 0.5
130
+ self.capsule_radius = sw * 0.15
131
+ self.capsule_x = sw / 2
132
+ self.capsule_y = (sh - self.capsule_height) / 2
133
+
105
134
  def _ensure_fonts(self) -> None:
106
135
  """Initialize fonts"""
107
136
  if self._font is None:
@@ -182,6 +211,7 @@ class ShapeEditor:
182
211
  def resize(self, rect: Rect) -> None:
183
212
  """Update rect"""
184
213
  self.rect = rect
214
+ self._center_view()
185
215
 
186
216
  def handle_event(self, event: pygame.event.Event) -> bool:
187
217
  """Handle input events"""
@@ -190,6 +220,7 @@ class ShapeEditor:
190
220
  if not self.rect.collidepoint(mouse) and event.type not in (
191
221
  pygame.MOUSEBUTTONUP,
192
222
  pygame.MOUSEMOTION,
223
+ pygame.KEYDOWN,
193
224
  ):
194
225
  return False
195
226
 
@@ -214,14 +245,25 @@ class ShapeEditor:
214
245
  self.offset_y = self._pan_start_offset[1] + dy
215
246
  return True
216
247
 
217
- # Update hover state
218
- if self.rect.collidepoint(mouse):
219
- self.hover_handle = self._find_handle_at(mouse)
220
-
221
- # Drag handle
248
+ # Drag handle — always takes priority over shape drag
222
249
  if self.dragging_handle:
223
250
  self._drag_handle(mouse)
224
251
  return True
252
+
253
+ # Drag entire shape via move icon
254
+ if self._dragging_shape:
255
+ dx = (mouse[0] - self._drag_start[0]) / self.zoom
256
+ dy = (mouse[1] - self._drag_start[1]) / self.zoom
257
+ self._apply_shape_delta(dx, dy)
258
+ return True
259
+
260
+ # Update hover state
261
+ if self.rect.collidepoint(mouse):
262
+ handle = self._find_handle_at(mouse)
263
+ if handle:
264
+ self.hover_handle = handle
265
+ else:
266
+ self.hover_handle = None
225
267
 
226
268
  # Mouse wheel to zoom
227
269
  if event.type == pygame.MOUSEWHEEL and self.rect.collidepoint(mouse):
@@ -238,10 +280,25 @@ class ShapeEditor:
238
280
  # Left click
239
281
  if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
240
282
  if self.rect.collidepoint(mouse):
283
+ # Pan mode — start dragging
284
+ if self._pan_mode:
285
+ self._panning = True
286
+ self._pan_start = mouse
287
+ self._pan_start_offset = (self.offset_x, self.offset_y)
288
+ return True
289
+
241
290
  # Check if clicking on handle
242
291
  handle = self._find_handle_at(mouse)
243
292
  if handle:
244
293
  self.dragging_handle = handle
294
+ self._dragging_shape = False
295
+ return True
296
+
297
+ # Check if clicking on move icon
298
+ if self._is_on_move_icon(mouse) and not self._pan_mode:
299
+ self._dragging_shape = True
300
+ self._drag_start = mouse
301
+ self._save_shape_positions()
245
302
  return True
246
303
 
247
304
  # Polygon mode - add vertex
@@ -256,6 +313,12 @@ class ShapeEditor:
256
313
  return True
257
314
 
258
315
  if event.type == pygame.MOUSEBUTTONUP and event.button == 1:
316
+ if self._panning:
317
+ self._panning = False
318
+ return True
319
+ if self._dragging_shape:
320
+ self._dragging_shape = False
321
+ return True
259
322
  if self.dragging_handle:
260
323
  self.dragging_handle = None
261
324
  return True
@@ -270,7 +333,13 @@ class ShapeEditor:
270
333
 
271
334
  # Keyboard shortcuts
272
335
  if event.type == pygame.KEYDOWN:
273
- if event.key == pygame.K_g:
336
+ if event.key == pygame.K_SPACE:
337
+ self._pan_mode = not self._pan_mode
338
+ if self._pan_mode:
339
+ self._pan_start = mouse
340
+ self._pan_start_offset = (self.offset_x, self.offset_y)
341
+ return True
342
+ elif event.key == pygame.K_g:
274
343
  self.show_grid = not self.show_grid
275
344
  return True
276
345
  elif event.key == pygame.K_r:
@@ -344,14 +413,22 @@ class ShapeEditor:
344
413
 
345
414
  if self.shape_type == "rectangle":
346
415
  if self.dragging_handle == "tl":
416
+ old_right = self.rect_x + self.rect_w
417
+ old_bottom = self.rect_y + self.rect_h
347
418
  self.rect_x = max(0, min(sw, sprite_pos[0]))
348
419
  self.rect_y = max(0, min(sh, sprite_pos[1]))
420
+ self.rect_w = max(1, old_right - self.rect_x)
421
+ self.rect_h = max(1, old_bottom - self.rect_y)
349
422
  elif self.dragging_handle == "tr":
423
+ old_bottom = self.rect_y + self.rect_h
350
424
  self.rect_w = max(1, sprite_pos[0] - self.rect_x)
351
425
  self.rect_y = max(0, min(sh, sprite_pos[1]))
426
+ self.rect_h = max(1, old_bottom - self.rect_y)
352
427
  elif self.dragging_handle == "bl":
428
+ old_right = self.rect_x + self.rect_w
353
429
  self.rect_x = max(0, min(sw, sprite_pos[0]))
354
430
  self.rect_h = max(1, sprite_pos[1] - self.rect_y)
431
+ self.rect_w = max(1, old_right - self.rect_x)
355
432
  elif self.dragging_handle == "br":
356
433
  self.rect_w = max(1, sprite_pos[0] - self.rect_x)
357
434
  self.rect_h = max(1, sprite_pos[1] - self.rect_y)
@@ -368,12 +445,102 @@ class ShapeEditor:
368
445
 
369
446
  elif self.shape_type == "capsule":
370
447
  if self.dragging_handle == "top":
448
+ old_bottom = self.capsule_y + self.capsule_height
371
449
  self.capsule_y = max(0, min(sh, sprite_pos[1]))
450
+ self.capsule_height = max(1, old_bottom - self.capsule_y)
372
451
  elif self.dragging_handle == "bottom":
373
452
  self.capsule_height = max(1, sprite_pos[1] - self.capsule_y)
374
453
  elif self.dragging_handle == "radius":
375
454
  self.capsule_radius = max(1, abs(sprite_pos[0] - self.capsule_x))
376
455
 
456
+ def _get_move_icon_pos(self) -> Tuple[int, int]:
457
+ """Get screen position for the move icon at mid-top of the shape"""
458
+ if self.shape_type == "rectangle":
459
+ mid_x = self.rect_x + self.rect_w / 2
460
+ top_y = self.rect_y
461
+ return self._sprite_to_screen((mid_x, top_y))
462
+ elif self.shape_type == "circle":
463
+ return self._sprite_to_screen((self.circle_x, self.circle_y - self.circle_radius))
464
+ elif self.shape_type == "capsule":
465
+ return self._sprite_to_screen((self.capsule_x, self.capsule_y))
466
+ elif self.shape_type == "polygon" and len(self.polygon_vertices) >= 3:
467
+ # Top-center of polygon bounding box
468
+ min_y = min(v[1] for v in self.polygon_vertices)
469
+ mid_x = (min(v[0] for v in self.polygon_vertices) + max(v[0] for v in self.polygon_vertices)) / 2
470
+ return self._sprite_to_screen((mid_x, min_y))
471
+ return (0, 0)
472
+
473
+ def _is_on_move_icon(self, screen_pos: Tuple[int, int]) -> bool:
474
+ """Check if a screen position is on the move icon (16x16 hit area)"""
475
+ icon_pos = self._get_move_icon_pos()
476
+ self._move_icon_pos = icon_pos
477
+ hit_size = 16
478
+ half = hit_size // 2
479
+ return (icon_pos[0] - half <= screen_pos[0] <= icon_pos[0] + half and
480
+ icon_pos[1] - half <= screen_pos[1] <= icon_pos[1] + half)
481
+
482
+ def _is_inside_shape(self, screen_pos: Tuple[int, int]) -> bool:
483
+ """Check if a screen position is inside the current shape (for hover detection)"""
484
+ if self.shape_type == "rectangle":
485
+ tl = self._sprite_to_screen((self.rect_x, self.rect_y))
486
+ br = self._sprite_to_screen((self.rect_x + self.rect_w, self.rect_y + self.rect_h))
487
+ return (tl[0] <= screen_pos[0] <= br[0] and tl[1] <= screen_pos[1] <= br[1])
488
+
489
+ elif self.shape_type == "circle":
490
+ center = self._sprite_to_screen((self.circle_x, self.circle_y))
491
+ radius = int(self.circle_radius * self.zoom)
492
+ return math.hypot(screen_pos[0] - center[0], screen_pos[1] - center[1]) <= radius
493
+
494
+ elif self.shape_type == "capsule":
495
+ top = self._sprite_to_screen((self.capsule_x, self.capsule_y))
496
+ bottom = self._sprite_to_screen((self.capsule_x, self.capsule_y + self.capsule_height))
497
+ radius = int(self.capsule_radius * self.zoom)
498
+ if math.hypot(screen_pos[0] - top[0], screen_pos[1] - top[1]) <= radius:
499
+ return True
500
+ if math.hypot(screen_pos[0] - bottom[0], screen_pos[1] - bottom[1]) <= radius:
501
+ return True
502
+ left = top[0] - radius
503
+ right = top[0] + radius
504
+ return left <= screen_pos[0] <= right and top[1] <= screen_pos[1] <= bottom[1]
505
+
506
+ elif self.shape_type == "polygon" and len(self.polygon_vertices) >= 3:
507
+ px, py = self._screen_to_sprite(screen_pos)
508
+ n = len(self.polygon_vertices)
509
+ inside = False
510
+ j = n - 1
511
+ for i in range(n):
512
+ vi = self.polygon_vertices[i]
513
+ vj = self.polygon_vertices[j]
514
+ if ((vi[1] > py) != (vj[1] > py)) and (
515
+ px < (vj[0] - vi[0]) * (py - vi[1]) / (vj[1] - vi[1]) + vi[0]
516
+ ):
517
+ inside = not inside
518
+ j = i
519
+ return inside
520
+
521
+ return False
522
+
523
+ def _save_shape_positions(self) -> None:
524
+ """Store current shape positions for delta calculation"""
525
+ self._drag_start_positions = {
526
+ "rect": (self.rect_x, self.rect_y),
527
+ "circle": (self.circle_x, self.circle_y),
528
+ "capsule": (self.capsule_x, self.capsule_y),
529
+ }
530
+
531
+ def _apply_shape_delta(self, dx: float, dy: float) -> None:
532
+ """Move shape by delta in sprite-local coordinates"""
533
+ sw, sh = self.sprite_surface.get_size()
534
+ if self.shape_type == "rectangle":
535
+ self.rect_x = max(0, min(sw - self.rect_w, self._drag_start_positions["rect"][0] + dx))
536
+ self.rect_y = max(0, min(sh - self.rect_h, self._drag_start_positions["rect"][1] + dy))
537
+ elif self.shape_type == "circle":
538
+ self.circle_x = max(0, min(sw, self._drag_start_positions["circle"][0] + dx))
539
+ self.circle_y = max(0, min(sh, self._drag_start_positions["circle"][1] + dy))
540
+ elif self.shape_type == "capsule":
541
+ self.capsule_x = max(0, min(sw, self._drag_start_positions["capsule"][0] + dx))
542
+ self.capsule_y = max(0, min(sh - self.capsule_height, self._drag_start_positions["capsule"][1] + dy))
543
+
377
544
  def draw(self, screen: pygame.Surface) -> None:
378
545
  """Draw the shape editor"""
379
546
  self._ensure_fonts()
@@ -406,6 +573,14 @@ class ShapeEditor:
406
573
  # Draw shape
407
574
  self._draw_shape(screen)
408
575
 
576
+ # Draw move icon at mid-top when hovering over shape body
577
+ if not self._pan_mode and not self.hover_handle:
578
+ mouse = pygame.mouse.get_pos()
579
+ if self.rect.collidepoint(mouse) and self._is_inside_shape(mouse):
580
+ icon_pos = self._get_move_icon_pos()
581
+ icon = icon_manager.get_icon("ToolMove", 16, _COLORS["shape_stroke"])
582
+ screen.blit(icon, (icon_pos[0] - 8, icon_pos[1] - 8))
583
+
409
584
  # Draw status
410
585
  self._draw_status(screen)
411
586
 
@@ -544,6 +719,10 @@ class ShapeEditor:
544
719
  def _draw_status(self, screen: pygame.Surface) -> None:
545
720
  """Draw status text"""
546
721
  lines = []
722
+
723
+ if self._pan_mode:
724
+ lines.append("PANNING MODE — drag to pan | Space to exit")
725
+
547
726
  lines.append(f"Shape: {self.shape_type.upper()} | Zoom: {self.zoom:.1f}x")
548
727
 
549
728
  if self.shape_type == "polygon":
@@ -554,7 +733,7 @@ class ShapeEditor:
554
733
  else:
555
734
  lines.append("Click to add vertices")
556
735
 
557
- lines.append("G: toggle grid | R: reset view | Mouse wheel: zoom")
736
+ lines.append("G: toggle grid | R: reset view | Space: pan mode | Wheel: zoom")
558
737
 
559
738
  y = self.rect.y + 5
560
739
  for line in lines:
@@ -39,6 +39,7 @@ from widgets.ui.status_bar import StatusBar, StatusType
39
39
  from widgets.ui.mode_indicator import ModeIndicator, Mode
40
40
  from widgets.ui.draw_utils import draw_panel, draw_button
41
41
  from widgets.ui.theme import COLORS, FONTS, SHAPE
42
+ from widgets.ui.collision_layer_sidebar import CollisionLayerSidebar
42
43
  from widgets.input import InlineTextInput
43
44
  from utils.font_manager import font_manager, FontWeight
44
45
  from utils.icon_manager import icon_manager
@@ -190,6 +191,15 @@ class ObjectTilesetCollisionEditor:
190
191
  show_icons=True,
191
192
  )
192
193
 
194
+ # Collision layer/mask sidebar (toggleable overlay)
195
+ self.layer_sidebar = CollisionLayerSidebar(
196
+ self.rect,
197
+ max_layers=16,
198
+ initial_layer=1,
199
+ initial_mask=0xFFFF,
200
+ on_changed=self._on_layer_mask_changed,
201
+ )
202
+
193
203
  # Collision painter (middle area)
194
204
  self.painter = CollisionPainter(
195
205
  self.painter_rect,
@@ -245,6 +255,8 @@ class ObjectTilesetCollisionEditor:
245
255
  self.status_bar.resize(self.status_bar_rect)
246
256
  if hasattr(self, 'painter'):
247
257
  self.painter.resize(self.painter_rect)
258
+ if hasattr(self, 'layer_sidebar'):
259
+ self.layer_sidebar.resize(self.rect)
248
260
 
249
261
  # Mode indicator position (in toolbar, centered)
250
262
  if hasattr(self, 'mode_indicator'):
@@ -332,6 +344,14 @@ class ObjectTilesetCollisionEditor:
332
344
  """Called when region selection changes"""
333
345
  self._current_region_id = region_id
334
346
 
347
+ # Load layer/mask from region properties
348
+ if region_id and region_id in self.library.regions:
349
+ data = self.library.regions[region_id]
350
+ layer = data.properties.get("collision_layer", 1)
351
+ mask = data.properties.get("collision_mask", 0xFFFF)
352
+ self.layer_sidebar.set_layer(layer)
353
+ self.layer_sidebar.set_mask(mask)
354
+
335
355
  # Update painter to show selected region
336
356
  if self._mode == EditorMode.PAINT_COLLISION:
337
357
  self._update_painter_for_current_region()
@@ -366,6 +386,12 @@ class ObjectTilesetCollisionEditor:
366
386
 
367
387
  return status
368
388
 
389
+ def _on_layer_mask_changed(self, layer: int, mask: int) -> None:
390
+ """Called when collision layer/mask widget changes."""
391
+ if self._current_region_id and self._current_region_id in self.library.regions:
392
+ self.library.regions[self._current_region_id].properties["collision_layer"] = layer
393
+ self.library.regions[self._current_region_id].properties["collision_mask"] = mask
394
+
369
395
  # === Rename Functionality ===
370
396
 
371
397
  def _start_rename(self, region_id: str) -> None:
@@ -805,6 +831,19 @@ class ObjectTilesetCollisionEditor:
805
831
  if self.region_selector.handle_event(event):
806
832
  return True
807
833
 
834
+ # Priority 5.5: Collision layer/mask sidebar (toggle + events when open)
835
+ if self.layer_sidebar.handle_event(event):
836
+ return True
837
+ if self.layer_sidebar.handle_toggle_event(event):
838
+ return True
839
+
840
+ # Keyboard shortcut: L to toggle sidebar
841
+ if event.type == pygame.KEYDOWN and event.key == pygame.K_l:
842
+ mods = pygame.key.get_mods()
843
+ if not (mods & (pygame.KMOD_CTRL | pygame.KMOD_LMETA)):
844
+ self.layer_sidebar.toggle()
845
+ return True
846
+
808
847
  # Priority 6: Collision painter (paint collision mode)
809
848
  if self._mode == EditorMode.PAINT_COLLISION:
810
849
  if self.painter.handle_event(event):
@@ -902,6 +941,12 @@ class ObjectTilesetCollisionEditor:
902
941
  # Draw status bar
903
942
  self.status_bar.draw(screen)
904
943
 
944
+ # Draw toggle button
945
+ self.layer_sidebar.draw_toggle_button(screen)
946
+
947
+ # Draw collision layer/mask sidebar (only when visible)
948
+ self.layer_sidebar.draw(screen)
949
+
905
950
  # Draw region selector (bottom)
906
951
  self.region_selector.draw(screen)
907
952
 
@@ -226,11 +226,13 @@ class Tilemap:
226
226
  if path_obj.is_absolute():
227
227
  target_path = path_obj
228
228
  else:
229
- data_root = getattr(getattr(self, 'editor', None), 'data_root', None)
229
+ data_root = getattr(getattr(self, "editor", None), "data_root", None)
230
230
  if data_root:
231
231
  target_path = data_root / path_obj
232
232
  else:
233
- raise RuntimeError("Cannot determine data_root - Editor not initialized with settings.json")
233
+ raise RuntimeError(
234
+ "Cannot determine data_root - Editor not initialized with settings.json"
235
+ )
234
236
 
235
237
  self.active_project_path = target_path
236
238
  elif self.active_project_path:
@@ -241,7 +243,7 @@ class Tilemap:
241
243
  if not target_path.parent.exists():
242
244
  target_path.parent.mkdir(parents=True, exist_ok=True)
243
245
 
244
- base_path = self._project_base_path()
246
+ map_dir = target_path.parent
245
247
 
246
248
  save_data = {
247
249
  "meta": {
@@ -279,7 +281,7 @@ class Tilemap:
279
281
 
280
282
  if hasattr(self.editor, "tileset_widget") and self.editor.tileset_widget:
281
283
  for ts in self.editor.tileset_widget.tilesets:
282
- path_str = to_project_path(ts.path, base_path)
284
+ path_str = to_project_path(ts.path, map_dir)
283
285
 
284
286
  ts_data: Dict[str, Any] = {"path": path_str, "type": ts.tileset_type}
285
287
  if ts.properties:
@@ -301,7 +303,7 @@ class Tilemap:
301
303
  {
302
304
  "name": group.name,
303
305
  "rules": [
304
- self._serialize_autotile_rule(rule, base_path)
306
+ self._serialize_autotile_rule(rule, map_dir)
305
307
  for rule in group.rules
306
308
  ],
307
309
  }
@@ -407,14 +409,16 @@ class Tilemap:
407
409
  return data
408
410
 
409
411
  def _path_matches_project_path(self, stored_path: str, actual_path: Path) -> bool:
410
- base_path = self._project_base_path()
412
+ map_dir = (
413
+ self.active_project_path.parent if self.active_project_path else Path()
414
+ )
411
415
  stored = Path(stored_path).expanduser()
412
416
  actual = Path(actual_path).expanduser()
413
417
 
414
418
  if stored.is_absolute():
415
419
  return stored.resolve() == actual.resolve()
416
420
 
417
- return stored.as_posix() == to_project_path(actual, base_path)
421
+ return stored.as_posix() == to_project_path(actual, map_dir)
418
422
 
419
423
  def _resolve_rule_resources(self, rule: "AutotileRule", ts_widget):
420
424
  resolved_ts = None
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><g fill="#e0e0e0"><circle cx="8" cy="8" r="2"/><path d="M8 1a1 1 0 0 0-.691.291l-2 2 1.414 1.414 1.293-1.293 1.293 1.293 1.414-1.414-2-2A1 1 0 0 0 8 1zM3.309 5.291l-2 2a1 1 0 0 0 0 1.414l2 2 1.414-1.414L3.43 7.998l1.293-1.293-1.414-1.414zm9.414 0-1.414 1.414 1.293 1.293-1.293 1.293 1.414 1.414 2-2a1 1 0 0 0 0-1.414l-2-2zm-6 6-1.414 1.414 2 2a1 1 0 0 0 1.414 0l2-2-1.414-1.414-1.293 1.293-1.293-1.293z"/></g></svg>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tilemap-editor
3
- Version: 3.0.1
3
+ Version: 3.1.2
4
4
  Summary: Pygame tilemap editor with SVG icons, professional UI, and sprite animation tools
5
5
  Author: tilemap editor contributors
6
6
  License: Proprietary
@@ -101,6 +101,7 @@ src/tilemap_editor/assets/fonts/noto/NotoSans-Bold.ttf
101
101
  src/tilemap_editor/assets/fonts/noto/NotoSans-BoldItalic.ttf
102
102
  src/tilemap_editor/assets/fonts/noto/NotoSans-Italic.ttf
103
103
  src/tilemap_editor/assets/fonts/noto/NotoSans-Regular.ttf
104
+ src/tilemap_editor/assets/icons/ToolMove.svg
104
105
  src/tilemap_editor/assets/icons/arrow-down.svg
105
106
  src/tilemap_editor/assets/icons/check.svg
106
107
  src/tilemap_editor/assets/icons/checked.svg
@@ -156,6 +157,8 @@ src/widgets/mapsetup.py
156
157
  src/widgets/regex_automap_designer.py
157
158
  src/widgets/tile_grid.py
158
159
  src/widgets/tile_selector.py
160
+ src/widgets/ui/collision_layer_mask.py
161
+ src/widgets/ui/collision_layer_sidebar.py
159
162
  src/widgets/ui/draw_utils.py
160
163
  src/widgets/ui/fileinput.py
161
164
  src/widgets/ui/layer_type_dialog.py
@@ -168,4 +171,7 @@ src/widgets/ui/status_bar.py
168
171
  src/widgets/ui/theme.py
169
172
  src/widgets/ui/tileset_type_dialog.py
170
173
  src/widgets/ui/toolbar.py
171
- src/widgets/ui/tooltip.py
174
+ src/widgets/ui/tooltip.py
175
+ tests/test_collision_layer_mask.py
176
+ tests/test_collision_layer_sidebar.py
177
+ tests/test_project_paths.py
@@ -10,7 +10,6 @@ standalone_filemanager
10
10
  standalone_image_viewer
11
11
  tilemap
12
12
  tilemap_editor
13
- tilemap_parser
14
13
  ttypes
15
14
  utils
16
15
  widgets