tilemap-editor 4.3.4__tar.gz → 4.3.6__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 (229) hide show
  1. {tilemap_editor-4.3.4/src/tilemap_editor.egg-info → tilemap_editor-4.3.6}/PKG-INFO +13 -1
  2. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/README.md +12 -0
  3. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/pyproject.toml +1 -1
  4. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/editor.py +2 -0
  5. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/node_manager.py +23 -7
  6. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/character_collision/editor.py +25 -2
  7. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/character_collision/shape_editor.py +39 -6
  8. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/object_tileset_collision/editor.py +3 -2
  9. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/tileset_collision/collision_painter.py +13 -6
  10. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/tileset_collision/editor.py +19 -4
  11. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap.py +2 -0
  12. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6/src/tilemap_editor.egg-info}/PKG-INFO +13 -1
  13. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor.egg-info/SOURCES.txt +1 -0
  14. tilemap_editor-4.3.6/src/utils/property_suggestions.py +70 -0
  15. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/layer_selector.py +1 -0
  16. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/tile_grid.py +3 -1
  17. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/tile_selector.py +5 -0
  18. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/node_editor.py +2 -0
  19. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/property_editor.py +118 -18
  20. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/region_selector.py +23 -12
  21. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/LICENSE +0 -0
  22. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/MANIFEST.in +0 -0
  23. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
  24. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
  25. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
  26. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
  27. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
  28. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
  29. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
  30. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
  31. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/arrow-down.svg +0 -0
  32. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/check.svg +0 -0
  33. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/checked.svg +0 -0
  34. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/close.svg +0 -0
  35. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/duplicate.svg +0 -0
  36. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/error.svg +0 -0
  37. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/file.svg +0 -0
  38. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/filedead.svg +0 -0
  39. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/fit.svg +0 -0
  40. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/folder.svg +0 -0
  41. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/grid.svg +0 -0
  42. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/image.svg +0 -0
  43. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/info.svg +0 -0
  44. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/load.svg +0 -0
  45. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/loop.svg +0 -0
  46. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/onion.svg +0 -0
  47. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/pan.svg +0 -0
  48. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/pause.svg +0 -0
  49. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/pencil.svg +0 -0
  50. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/play.svg +0 -0
  51. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/plus.svg +0 -0
  52. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/radio.svg +0 -0
  53. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/reset.svg +0 -0
  54. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/save.svg +0 -0
  55. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/skip-back.svg +0 -0
  56. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/skip-forward.svg +0 -0
  57. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/stop.svg +0 -0
  58. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/tilemap.svg +0 -0
  59. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/tileset.svg +0 -0
  60. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/unchecked.svg +0 -0
  61. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/warning.svg +0 -0
  62. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/zoomin.svg +0 -0
  63. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/zoomout.svg +0 -0
  64. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/setup.cfg +0 -0
  65. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/constants.py +0 -0
  66. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/event_map.py +0 -0
  67. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/layers.py +0 -0
  68. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/main.py +0 -0
  69. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/nodes.py +0 -0
  70. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/__init__.py +0 -0
  71. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/character_collision/__init__.py +0 -0
  72. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/character_collision/models.py +0 -0
  73. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/character_collision/protocols.py +0 -0
  74. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/character_collision/standalone.py +0 -0
  75. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/object_tileset_collision/__init__.py +0 -0
  76. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/object_tileset_collision/models.py +0 -0
  77. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/object_tileset_collision/protocols.py +0 -0
  78. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/object_tileset_collision/standalone.py +0 -0
  79. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_animation/__init__.py +0 -0
  80. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_animation/__main__.py +0 -0
  81. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_animation/clipboard_util.py +0 -0
  82. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_animation/editor.py +0 -0
  83. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_animation/frame_picker.py +0 -0
  84. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_animation/models.py +0 -0
  85. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_animation/preview.py +0 -0
  86. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_animation/protocols.py +0 -0
  87. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_animation/runtime_load.py +0 -0
  88. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_animation/standalone.py +0 -0
  89. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_animation/timeline.py +0 -0
  90. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_animation/validation.py +0 -0
  91. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_editor/__init__.py +0 -0
  92. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_editor/dialogs.py +0 -0
  93. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_editor/editor.py +0 -0
  94. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_editor/region_export.py +0 -0
  95. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_editor/standalone.py +0 -0
  96. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/tileset_collision/__init__.py +0 -0
  97. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/tileset_collision/models.py +0 -0
  98. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/tileset_collision/protocols.py +0 -0
  99. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/tileset_collision/standalone.py +0 -0
  100. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/standalone_automap.py +0 -0
  101. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/standalone_error_console.py +0 -0
  102. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/standalone_filemanager.py +0 -0
  103. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/standalone_image_viewer.py +0 -0
  104. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/__init__.py +0 -0
  105. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/__main__.py +0 -0
  106. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/__init__.py +0 -0
  107. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
  108. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
  109. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
  110. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
  111. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
  112. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
  113. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
  114. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
  115. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/ToolMove.svg +0 -0
  116. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/arrow-down.svg +0 -0
  117. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/check.svg +0 -0
  118. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/checked.svg +0 -0
  119. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/close.svg +0 -0
  120. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/duplicate.svg +0 -0
  121. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/error.svg +0 -0
  122. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/file.svg +0 -0
  123. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/filedead.svg +0 -0
  124. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/fit.svg +0 -0
  125. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/fold_down_arrow.svg +0 -0
  126. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/folder.svg +0 -0
  127. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/gear.svg +0 -0
  128. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/grid.svg +0 -0
  129. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/image.svg +0 -0
  130. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/info.svg +0 -0
  131. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/load.svg +0 -0
  132. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/loop.svg +0 -0
  133. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/miniobj.svg +0 -0
  134. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/onion.svg +0 -0
  135. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/pan.svg +0 -0
  136. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/pause.svg +0 -0
  137. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/pencil.svg +0 -0
  138. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/play.svg +0 -0
  139. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/plus.svg +0 -0
  140. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/radio.svg +0 -0
  141. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/reset.svg +0 -0
  142. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/save.svg +0 -0
  143. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/skip-back.svg +0 -0
  144. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/skip-forward.svg +0 -0
  145. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/stop.svg +0 -0
  146. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/tilemap.svg +0 -0
  147. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/tileset.svg +0 -0
  148. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/unchecked.svg +0 -0
  149. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/unfold_right_arrow.svg +0 -0
  150. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/warning.svg +0 -0
  151. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/zoomin.svg +0 -0
  152. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/zoomout.svg +0 -0
  153. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/cli.py +0 -0
  154. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/main.py +0 -0
  155. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/settings.py +0 -0
  156. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor.egg-info/dependency_links.txt +0 -0
  157. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor.egg-info/entry_points.txt +0 -0
  158. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor.egg-info/requires.txt +0 -0
  159. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor.egg-info/top_level.txt +0 -0
  160. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/ttypes/__init__.py +0 -0
  161. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/ttypes/tilemap.py +0 -0
  162. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/utils/__init__.py +0 -0
  163. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/utils/editor_preference.py +0 -0
  164. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/utils/error_handler.py +0 -0
  165. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/utils/font_manager.py +0 -0
  166. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/utils/history.py +0 -0
  167. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/utils/icon_manager.py +0 -0
  168. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/utils/icons_cache.py +0 -0
  169. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/utils/log_capture.py +0 -0
  170. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/utils/project_paths.py +0 -0
  171. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/utils/serialization.py +0 -0
  172. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/utils/standalone.py +0 -0
  173. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/utils/validation.py +0 -0
  174. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/__init__.py +0 -0
  175. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/automap_models.py +0 -0
  176. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/autotile_template.py +0 -0
  177. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/autotiler.py +0 -0
  178. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/filemanager.py +0 -0
  179. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/input.py +0 -0
  180. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/map_properties.py +0 -0
  181. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/mapsetup.py +0 -0
  182. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/particle_presets.py +0 -0
  183. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/particle_system.py +0 -0
  184. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/regex_automap_designer.py +0 -0
  185. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/spritesheet_grid.py +0 -0
  186. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/button.py +0 -0
  187. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/checkbox.py +0 -0
  188. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/collision_layer_mask.py +0 -0
  189. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/collision_layer_sidebar.py +0 -0
  190. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/confirm_dialog.py +0 -0
  191. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/dialog_base.py +0 -0
  192. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/drag_tracker.py +0 -0
  193. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/draw_utils.py +0 -0
  194. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/fileinput.py +0 -0
  195. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/label.py +0 -0
  196. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/layer_type_dialog.py +0 -0
  197. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/menubar.py +0 -0
  198. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/mode_indicator.py +0 -0
  199. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/node_selector.py +0 -0
  200. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/notification.py +0 -0
  201. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/particle_config_dialog.py +0 -0
  202. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/scrollbar.py +0 -0
  203. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/sidebar_container.py +0 -0
  204. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/splitter.py +0 -0
  205. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/status_bar.py +0 -0
  206. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/theme.py +0 -0
  207. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/tileset_type_dialog.py +0 -0
  208. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/toast.py +0 -0
  209. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/tool_manager.py +0 -0
  210. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/toolbar.py +0 -0
  211. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/tooltip.py +0 -0
  212. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/tree_widget.py +0 -0
  213. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/widget_base.py +0 -0
  214. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_animation_library_grid_offset.py +0 -0
  215. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_collision_layer_mask.py +0 -0
  216. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_collision_layer_sidebar.py +0 -0
  217. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_drag_tracker.py +0 -0
  218. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_editor_pan_mode.py +0 -0
  219. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_gid_collision.py +0 -0
  220. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_layers_autotile.py +0 -0
  221. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_project_paths.py +0 -0
  222. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_render_scale.py +0 -0
  223. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_serialization_regression.py +0 -0
  224. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_sprite_animation_editor_grid.py +0 -0
  225. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_tile_grid_selection.py +0 -0
  226. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_tile_selector_pick.py +0 -0
  227. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_tilemap_save.py +0 -0
  228. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_timeline_invalidate_cache.py +0 -0
  229. {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_toolbar_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tilemap-editor
3
- Version: 4.3.4
3
+ Version: 4.3.6
4
4
  Summary: Pygame tilemap editor with collision, animation, sprite & particle tools
5
5
  Author: tilemap editor contributors
6
6
  License: Proprietary
@@ -18,6 +18,15 @@ Dynamic: license-file
18
18
 
19
19
  `tilemap-editor` is a pygame-based map editor focused on fast iteration for 2D games.
20
20
 
21
+ # tilemap-editor
22
+
23
+ `tilemap-editor` is a Pygame-based map editor focused on fast iteration for 2D games.
24
+
25
+ > **Credits:** The initial version of this project was based on DaFluffyPotato's platformer tutorial which is about 6-hours. If you want to understand the foundation this project started from, I highly recommend watching his video: https://www.youtube.com/watch?v=2gABYM5M0ww. While the editor has since evolved with many additional features and improvements, the original map format has been preserved for compatibility.
26
+
27
+ Several features and design ideas were also inspired by the Godot Engine, including the animation workflow, polygon collision editing, and character collision (Sprite2D-style) editing.
28
+
29
+
21
30
  ## Installation
22
31
 
23
32
  [tilemap-editor on PyPI](https://pypi.org/project/tilemap-editor/)
@@ -55,3 +64,6 @@ if __name__ == "__main__":
55
64
  editor = Editor()
56
65
  editor.run()
57
66
  ```
67
+
68
+ > [!WARNING]
69
+ > The current release requires **Python 3.11+**. The documentation incorrectly lists Python 3.10 support due to an accidental dependency on `typing.NotRequired`. This will be fixed in a future update.
@@ -2,6 +2,15 @@
2
2
 
3
3
  `tilemap-editor` is a pygame-based map editor focused on fast iteration for 2D games.
4
4
 
5
+ # tilemap-editor
6
+
7
+ `tilemap-editor` is a Pygame-based map editor focused on fast iteration for 2D games.
8
+
9
+ > **Credits:** The initial version of this project was based on DaFluffyPotato's platformer tutorial which is about 6-hours. If you want to understand the foundation this project started from, I highly recommend watching his video: https://www.youtube.com/watch?v=2gABYM5M0ww. While the editor has since evolved with many additional features and improvements, the original map format has been preserved for compatibility.
10
+
11
+ Several features and design ideas were also inspired by the Godot Engine, including the animation workflow, polygon collision editing, and character collision (Sprite2D-style) editing.
12
+
13
+
5
14
  ## Installation
6
15
 
7
16
  [tilemap-editor on PyPI](https://pypi.org/project/tilemap-editor/)
@@ -39,3 +48,6 @@ if __name__ == "__main__":
39
48
  editor = Editor()
40
49
  editor.run()
41
50
  ```
51
+
52
+ > [!WARNING]
53
+ > The current release requires **Python 3.11+**. The documentation incorrectly lists Python 3.10 support due to an accidental dependency on `typing.NotRequired`. This will be fixed in a future update.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tilemap-editor"
7
- version = "4.3.4"
7
+ version = "4.3.6"
8
8
  description = "Pygame tilemap editor with collision, animation, sprite & particle tools"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -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.property_suggestions import PropertySuggestionRegistry
24
25
  from utils.standalone import launch_standalone
25
26
  from widgets.autotiler import AutotileRuleDesigner
26
27
  from widgets.layer_selector import LayerSelector
@@ -212,6 +213,7 @@ class Editor:
212
213
  self.regex_automap_designer = RegexAutomapDesigner(self, 150, 100)
213
214
  self.notifications = NotificationManager(self)
214
215
  self.tooltip = TooltipManager()
216
+ self.suggestion_registry = PropertySuggestionRegistry()
215
217
  self.error_console_process: subprocess.Popen | None = None
216
218
  self.property_editor: PropertyEditor | None = None
217
219
  self.particle_config_dialog: ParticleConfigDialog | None = None
@@ -13,7 +13,7 @@ def create_node_id() -> str:
13
13
  return str(uuid.uuid4())
14
14
 
15
15
 
16
- SIDECAR_VERSION = 1
16
+ SIDECAR_VERSION = 2
17
17
 
18
18
 
19
19
  class NodeManager:
@@ -49,10 +49,15 @@ class NodeManager:
49
49
  raw = json.loads(sidecar.read_text(encoding="utf-8"))
50
50
  if not isinstance(raw, dict):
51
51
  return
52
+ version = raw.get("version", 1)
52
53
  self.groups = list(raw.get("groups", []))
54
+ rs = self.editor.tilemap.render_scale
53
55
  for item in raw.get("nodes", []):
54
56
  if isinstance(item, dict):
55
57
  node = Node.from_dict(item)
58
+ if version >= 2:
59
+ node.area.x = int(node.area.x * rs)
60
+ node.area.y = int(node.area.y * rs)
56
61
  self.nodes[node.node_id] = node
57
62
  except (json.JSONDecodeError, KeyError, ValueError, TypeError):
58
63
  import logging
@@ -62,12 +67,23 @@ class NodeManager:
62
67
  def save(self, map_path: Path) -> None:
63
68
  sidecar = self._sidecar_path_for(map_path)
64
69
  self.nodes_dir.mkdir(parents=True, exist_ok=True)
65
- data: dict[str, Any] = {
66
- "version": SIDECAR_VERSION,
67
- "groups": self.groups,
68
- "nodes": [node.to_dict() for node in self.nodes.values()],
69
- }
70
- sidecar.write_text(json.dumps(data, indent=2), encoding="utf-8")
70
+ rs = self.editor.tilemap.render_scale
71
+ orig_areas: dict[str, tuple[int, int]] = {}
72
+ for nid, node in self.nodes.items():
73
+ orig_areas[nid] = (node.area.x, node.area.y)
74
+ node.area.x = int(node.area.x / rs)
75
+ node.area.y = int(node.area.y / rs)
76
+ try:
77
+ data: dict[str, Any] = {
78
+ "version": SIDECAR_VERSION,
79
+ "groups": self.groups,
80
+ "nodes": [node.to_dict() for node in self.nodes.values()],
81
+ }
82
+ sidecar.write_text(json.dumps(data, indent=2), encoding="utf-8")
83
+ finally:
84
+ for nid, (ox, oy) in orig_areas.items():
85
+ self.nodes[nid].area.x = ox
86
+ self.nodes[nid].area.y = oy
71
87
 
72
88
  def add_node(self, node: Node) -> str:
73
89
  self.nodes[node.node_id] = node
@@ -182,11 +182,32 @@ class CharacterCollisionEditor:
182
182
  self._image_path = self._rel_path(path)
183
183
  pygame.display.set_caption(f"Character Collision Editor — {path.stem}")
184
184
  self._toast_manager.success(f"Loaded sprite: {path.name}")
185
+ self._auto_load_collision()
185
186
  except Exception as e:
186
187
  self._toast_manager.error(f"Failed to load image: {e}")
187
188
  finally:
188
189
  self._image_dialog = None
189
190
 
191
+ def _auto_load_collision(self) -> bool:
192
+ name = self._name_input.text.strip()
193
+ if not name:
194
+ return False
195
+ safe = re.sub(r"[^\w\-]+", "-", name).strip("-").lower()
196
+ coll_filename = f"{safe}.collision.json"
197
+ try:
198
+ coll_dir = self._get_collision_dir()
199
+ except RuntimeError:
200
+ return False
201
+ coll_path = coll_dir / coll_filename
202
+ if coll_path.exists():
203
+ try:
204
+ self.load_from_file(coll_path)
205
+ self._toast_manager.success(f"Loaded collision: {coll_path.name}")
206
+ return True
207
+ except Exception:
208
+ pass
209
+ return False
210
+
190
211
  def _open_image_dialog(self) -> None:
191
212
  try:
192
213
  data_root = self._data_root or Path.cwd()
@@ -281,7 +302,6 @@ class CharacterCollisionEditor:
281
302
  self.character_name = data.name
282
303
  self._name_input.text = data.name
283
304
  self._name_input.cursor_pos = len(data.name)
284
- self.shape_editor.load_shape_data(data.shape.to_dict())
285
305
  layer = data.properties.get("collision_layer", 1)
286
306
  mask = data.properties.get("collision_mask", 0xFFFF)
287
307
  self.layer_sidebar.set_layer(layer)
@@ -293,7 +313,9 @@ class CharacterCollisionEditor:
293
313
  if resolved:
294
314
  self._load_sprite_from_path(resolved)
295
315
  self._toast_manager.success(f"Sprite loaded: {resolved.name}")
296
- return
316
+
317
+ self.shape_editor.load_shape_data(data.shape.to_dict())
318
+ return
297
319
 
298
320
  if data.name:
299
321
  for d in [self._data_root]:
@@ -545,6 +567,7 @@ class CharacterCollisionEditor:
545
567
  editor = cls(rect, surface, character_name)
546
568
  editor._data_root = data_root
547
569
  editor._image_path = image_path
570
+ editor._auto_load_collision()
548
571
  return editor
549
572
 
550
573
  def run(self) -> None:
@@ -100,6 +100,30 @@ class ShapeEditor:
100
100
  self.offset_x = (self.rect.w - sw * self.zoom) / 2
101
101
  self.offset_y = (self.rect.h - sh * self.zoom) / 2
102
102
 
103
+ def _focus_on_shape(self) -> None:
104
+ """Center the viewport on the shape's bounding box"""
105
+ left, top, right, bottom = self._shape_bounds()
106
+ cx = (left + right) / 2
107
+ cy = (top + bottom) / 2
108
+ self.offset_x = self.rect.w / 2 - cx * self.zoom
109
+ self.offset_y = self.rect.h / 2 - cy * self.zoom
110
+
111
+ def _shape_bounds(self) -> tuple[float, float, float, float]:
112
+ """Get shape bounding box in sprite-local coords (left, top, right, bottom)"""
113
+ if self.shape_type == "rectangle":
114
+ return (self.rect_x, self.rect_y, self.rect_x + self.rect_w, self.rect_y + self.rect_h)
115
+ if self.shape_type == "circle":
116
+ return (self.circle_x - self.circle_radius, self.circle_y - self.circle_radius,
117
+ self.circle_x + self.circle_radius, self.circle_y + self.circle_radius)
118
+ if self.shape_type == "capsule":
119
+ return (self.capsule_x - self.capsule_radius, self.capsule_y,
120
+ self.capsule_x + self.capsule_radius, self.capsule_y + self.capsule_height)
121
+ if self.shape_type == "polygon" and self.polygon_vertices:
122
+ xs = [v[0] for v in self.polygon_vertices]
123
+ ys = [v[1] for v in self.polygon_vertices]
124
+ return (min(xs), min(ys), max(xs), max(ys))
125
+ return (0, 0, 0, 0)
126
+
103
127
  def _center_shape(self) -> None:
104
128
  """Center all shape types on the sprite"""
105
129
  sw, sh = self.sprite_surface.get_size()
@@ -195,6 +219,8 @@ class ShapeEditor:
195
219
  elif shape_type == "polygon":
196
220
  self.polygon_vertices = [tuple(v) for v in data.get("vertices", [])]
197
221
 
222
+ self._focus_on_shape()
223
+
198
224
  def resize(self, rect: Rect) -> None:
199
225
  """Update rect"""
200
226
  self.rect = rect
@@ -254,12 +280,19 @@ class ShapeEditor:
254
280
  self.hover_handle = None
255
281
 
256
282
  if event.type == pygame.MOUSEWHEEL and self.rect.collidepoint(mouse):
257
- self.zoom *= 1.15 if event.y > 0 else 0.87
258
- self.zoom = max(0.5, min(self.zoom, 8.0))
259
-
260
- sprite_pos = self._screen_to_sprite(mouse)
261
- self.offset_x = mouse[0] - self.rect.x - sprite_pos[0] * self.zoom
262
- self.offset_y = mouse[1] - self.rect.y - sprite_pos[1] * self.zoom
283
+ mods = pygame.key.get_mods()
284
+ if mods & (pygame.KMOD_CTRL | pygame.KMOD_META):
285
+ self.zoom *= 1.15 if event.y > 0 else 0.87
286
+ self.zoom = max(0.5, min(self.zoom, 8.0))
287
+ sprite_pos = self._screen_to_sprite(mouse)
288
+ self.offset_x = mouse[0] - self.rect.x - sprite_pos[0] * self.zoom
289
+ self.offset_y = mouse[1] - self.rect.y - sprite_pos[1] * self.zoom
290
+ elif mods & pygame.KMOD_SHIFT:
291
+ scroll_val = event.y if event.y != 0 else event.x
292
+ self.offset_x -= scroll_val * 30
293
+ else:
294
+ scroll_val = event.y if event.y != 0 else event.x
295
+ self.offset_y -= scroll_val * 30
263
296
  return True
264
297
 
265
298
  if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
@@ -234,12 +234,13 @@ class ObjectTilesetCollisionEditor:
234
234
  def _layout_toolbar_buttons(self) -> None:
235
235
  x = self.toolbar_rect.x + 10
236
236
  y = self.toolbar_rect.y + 8
237
- for i, btn in enumerate(self._toolbar_buttons):
237
+ for btn in self._toolbar_buttons:
238
238
  if btn.text == "?":
239
239
  w, h = 28, 28
240
240
  else:
241
241
  w, h = 60, 28
242
- btn.resize(x + i * (w + 8), y, w, h)
242
+ btn.resize(x, y, w, h)
243
+ x += w + 8
243
244
 
244
245
  def _on_splitter_drag(self, pos: int) -> None:
245
246
  self.tileset_selector_height = max(
@@ -476,12 +476,19 @@ class CollisionPainter:
476
476
  return True
477
477
 
478
478
  if event.type == pygame.MOUSEWHEEL and self.rect.collidepoint(mouse):
479
- self.zoom *= 1.15 if event.y > 0 else 0.87
480
- self.zoom = max(0.5, min(self.zoom, 8.0))
481
-
482
- tile_pos = self._screen_to_tile(mouse)
483
- self.offset_x = mouse[0] - self.rect.x - tile_pos[0] * self.zoom
484
- self.offset_y = mouse[1] - self.rect.y - tile_pos[1] * self.zoom
479
+ mods = pygame.key.get_mods()
480
+ if mods & (pygame.KMOD_CTRL | pygame.KMOD_META):
481
+ self.zoom *= 1.15 if event.y > 0 else 0.87
482
+ self.zoom = max(0.5, min(self.zoom, 8.0))
483
+ tile_pos = self._screen_to_tile(mouse)
484
+ self.offset_x = mouse[0] - self.rect.x - tile_pos[0] * self.zoom
485
+ self.offset_y = mouse[1] - self.rect.y - tile_pos[1] * self.zoom
486
+ elif mods & pygame.KMOD_SHIFT:
487
+ scroll_val = event.y if event.y != 0 else event.x
488
+ self.offset_x -= scroll_val * 30
489
+ else:
490
+ scroll_val = event.y if event.y != 0 else event.x
491
+ self.offset_y -= scroll_val * 30
485
492
  return True
486
493
 
487
494
  if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
@@ -716,8 +716,18 @@ class TilesetCollisionEditor:
716
716
  return True
717
717
 
718
718
  if event.type == pygame.MOUSEWHEEL:
719
- self.tileset_zoom *= 1.15 if event.y > 0 else 0.87
720
- self.tileset_zoom = max(0.5, min(self.tileset_zoom, 8.0))
719
+ mods = pygame.key.get_mods()
720
+ if mods & (pygame.KMOD_CTRL | pygame.KMOD_META):
721
+ self.tileset_zoom *= 1.15 if event.y > 0 else 0.87
722
+ self.tileset_zoom = max(0.5, min(self.tileset_zoom, 8.0))
723
+ elif mods & pygame.KMOD_SHIFT:
724
+ scroll_val = event.y if event.y != 0 else event.x
725
+ self.tileset_scroll_x -= scroll_val * 30
726
+ else:
727
+ scroll_val = event.y if event.y != 0 else event.x
728
+ self.tileset_scroll_y -= scroll_val * 30
729
+ self.tileset_scroll_x = max(0, self.tileset_scroll_x)
730
+ self.tileset_scroll_y = max(0, self.tileset_scroll_y)
721
731
  return True
722
732
 
723
733
  if event.type == pygame.MOUSEBUTTONUP:
@@ -947,12 +957,17 @@ class TilesetCollisionEditor:
947
957
  tw = int(self._tile_size[0] * self.tileset_zoom)
948
958
  th = int(self._tile_size[1] * self.tileset_zoom)
949
959
 
960
+ tileset_scr_w = self.tile_cols * tw
961
+ tileset_scr_h = self.tile_rows * th
962
+ center_off_x = max(0, (self.tileset_selector_rect.w - tileset_scr_w) // 2)
963
+ center_off_y = max(0, (self.tileset_selector_rect.h - tileset_scr_h) // 2)
964
+
950
965
  for row in range(self.tile_rows):
951
966
  for col in range(self.tile_cols):
952
967
  tile_id = row * self.tile_cols + col
953
968
 
954
- x = self.tileset_selector_rect.x + col * tw - self.tileset_scroll_x
955
- y = self.tileset_selector_rect.y + row * th - self.tileset_scroll_y
969
+ x = self.tileset_selector_rect.x + col * tw - self.tileset_scroll_x + center_off_x
970
+ y = self.tileset_selector_rect.y + row * th - self.tileset_scroll_y + center_off_y
956
971
 
957
972
  if (
958
973
  x + tw < self.tileset_selector_rect.x
@@ -623,6 +623,8 @@ class Tilemap:
623
623
  if hasattr(self.editor, "node_selector"):
624
624
  self.editor.node_selector._rebuild_filter()
625
625
 
626
+ if hasattr(self.editor, "suggestion_registry"):
627
+ self.editor.suggestion_registry.refresh(self.editor)
626
628
  self.initialized = True
627
629
 
628
630
  if self.editor.tile_grid_widget:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tilemap-editor
3
- Version: 4.3.4
3
+ Version: 4.3.6
4
4
  Summary: Pygame tilemap editor with collision, animation, sprite & particle tools
5
5
  Author: tilemap editor contributors
6
6
  License: Proprietary
@@ -18,6 +18,15 @@ Dynamic: license-file
18
18
 
19
19
  `tilemap-editor` is a pygame-based map editor focused on fast iteration for 2D games.
20
20
 
21
+ # tilemap-editor
22
+
23
+ `tilemap-editor` is a Pygame-based map editor focused on fast iteration for 2D games.
24
+
25
+ > **Credits:** The initial version of this project was based on DaFluffyPotato's platformer tutorial which is about 6-hours. If you want to understand the foundation this project started from, I highly recommend watching his video: https://www.youtube.com/watch?v=2gABYM5M0ww. While the editor has since evolved with many additional features and improvements, the original map format has been preserved for compatibility.
26
+
27
+ Several features and design ideas were also inspired by the Godot Engine, including the animation workflow, polygon collision editing, and character collision (Sprite2D-style) editing.
28
+
29
+
21
30
  ## Installation
22
31
 
23
32
  [tilemap-editor on PyPI](https://pypi.org/project/tilemap-editor/)
@@ -55,3 +64,6 @@ if __name__ == "__main__":
55
64
  editor = Editor()
56
65
  editor.run()
57
66
  ```
67
+
68
+ > [!WARNING]
69
+ > The current release requires **Python 3.11+**. The documentation incorrectly lists Python 3.10 support due to an accidental dependency on `typing.NotRequired`. This will be fixed in a future update.
@@ -159,6 +159,7 @@ src/utils/icon_manager.py
159
159
  src/utils/icons_cache.py
160
160
  src/utils/log_capture.py
161
161
  src/utils/project_paths.py
162
+ src/utils/property_suggestions.py
162
163
  src/utils/serialization.py
163
164
  src/utils/standalone.py
164
165
  src/utils/validation.py
@@ -0,0 +1,70 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ if TYPE_CHECKING:
4
+ from editor import Editor
5
+
6
+
7
+ class PropertySuggestionRegistry:
8
+ def __init__(self):
9
+ self._data: dict[str, list[str]] = {}
10
+
11
+ def refresh(self, editor: "Editor"):
12
+ values: dict[str, set[str]] = {}
13
+
14
+ if editor.tileset_widget:
15
+ for ts in editor.tileset_widget.tilesets:
16
+ for k, v in ts.properties.items():
17
+ values.setdefault(k, set()).add(str(v))
18
+ for tile_props in ts.tile_properties.values():
19
+ for k, v in tile_props.items():
20
+ values.setdefault(k, set()).add(str(v))
21
+
22
+ if editor.tilemap:
23
+ for layer in editor.tilemap.layer_manager.layers:
24
+ for k, v in layer.properties.items():
25
+ values.setdefault(k, set()).add(str(v))
26
+ for tile in layer.tiles.values():
27
+ if "properties" in tile:
28
+ for k, v in tile["properties"].items():
29
+ values.setdefault(k, set()).add(str(v))
30
+ if layer.layer_type == "object":
31
+ for obj in layer.objects.values():
32
+ if "properties" in obj:
33
+ for k, v in obj["properties"].items():
34
+ values.setdefault(k, set()).add(str(v))
35
+
36
+ if editor.node_manager:
37
+ for node in editor.node_manager.nodes.values():
38
+ for k, v in node.properties.items():
39
+ values.setdefault(k, set()).add(str(v))
40
+
41
+ self._data = {k: sorted(v) for k, v in values.items()}
42
+
43
+ @property
44
+ def known_keys(self) -> list[str]:
45
+ return sorted(self._data.keys())
46
+
47
+ def ghost_text(self, key: str, input_text: str) -> str:
48
+ if key not in self._data:
49
+ return ""
50
+ prefix = input_text.lower()
51
+ matches = [
52
+ v
53
+ for v in self._data[key]
54
+ if v.lower().startswith(prefix) and v != input_text
55
+ ]
56
+ if matches:
57
+ return matches[0][len(input_text):]
58
+ return ""
59
+
60
+ def key_ghost(self, input_text: str) -> str:
61
+ if not input_text:
62
+ return ""
63
+ prefix = input_text.lower()
64
+ matches = [
65
+ k for k in self._data
66
+ if k.lower().startswith(prefix) and k != input_text
67
+ ]
68
+ if matches:
69
+ return sorted(matches)[0][len(input_text):]
70
+ return ""
@@ -408,6 +408,7 @@ class LayerSelector:
408
408
 
409
409
  def _save_layer_properties(self, layer: Layer, props: dict):
410
410
  layer.properties = props
411
+ self.editor.suggestion_registry.refresh(self.editor)
411
412
  print(f"Saved properties for layer: {layer.name}")
412
413
 
413
414
  def draw(self, screen: Surface) -> None:
@@ -799,10 +799,12 @@ class TileGrid:
799
799
  elif active_layer.layer_type == "object":
800
800
  mouse_pos = pygame.mouse.get_pos()
801
801
  world_pos = self.screen_to_world(mouse_pos)
802
+ rs = self.editor.tilemap.render_scale
803
+ pick_pos = (int(world_pos[0] / rs), int(world_pos[1] / rs)) if rs else world_pos
802
804
  for _obj_id, obj in active_layer.get_all_objects().items():
803
805
  area = obj["area"]
804
806
  obj_rect = Rect(area["x"], area["y"], area["w"], area["h"])
805
- if obj_rect.collidepoint(world_pos):
807
+ if obj_rect.collidepoint(pick_pos):
806
808
  ttype = int(obj["ttype"])
807
809
  variant = obj["variant"]
808
810
  ts_widget.select_tile_by_variant(ttype, variant)
@@ -416,6 +416,7 @@ class TileSelector(WidgetBase):
416
416
  self._tree.selected_ids = {tileset_data.uid}
417
417
  if tileset_type == "object":
418
418
  self.selected_tile = (0, 0, surf.get_width(), surf.get_height())
419
+ self.editor.suggestion_registry.refresh(self.editor)
419
420
  except Exception as e:
420
421
  error_handler.capture(e, context="load_tileset_surface")
421
422
 
@@ -479,6 +480,7 @@ class TileSelector(WidgetBase):
479
480
  self._sync_tree()
480
481
  self._tree.selected_ids = {tileset_data.uid}
481
482
 
483
+ self.editor.suggestion_registry.refresh(self.editor)
482
484
  delattr(self, "_pending_tileset_path")
483
485
  delattr(self, "_pending_tileset_surf")
484
486
  if self._pending_tileset_queue:
@@ -561,6 +563,7 @@ class TileSelector(WidgetBase):
561
563
  if not self.tilesets:
562
564
  self.active_idx = -1
563
565
  self._sync_tree()
566
+ self.editor.suggestion_registry.refresh(self.editor)
564
567
 
565
568
  def open_collision_editor(self):
566
569
  if self.active_idx == -1 or self.active_idx >= len(self.tilesets):
@@ -573,6 +576,7 @@ class TileSelector(WidgetBase):
573
576
 
574
577
  def _save_tileset_properties(self, ts: TilesetData, props: dict):
575
578
  ts.properties = props
579
+ self.editor.suggestion_registry.refresh(self.editor)
576
580
  print(f"Saved properties for tileset: {ts.name}")
577
581
 
578
582
  def _save_tile_properties(self, ts: TilesetData, variant_id: int, props: dict):
@@ -582,6 +586,7 @@ class TileSelector(WidgetBase):
582
586
  def _save_tile_properties_multi(self, ts: TilesetData, variant_ids: list[int], props: dict):
583
587
  for vid in variant_ids:
584
588
  ts.tile_properties[vid] = props.copy()
589
+ self.editor.suggestion_registry.refresh(self.editor)
585
590
  if len(variant_ids) == 1:
586
591
  print(f"Saved properties for tile {variant_ids[0]} in tileset: {ts.name}")
587
592
  else:
@@ -356,12 +356,14 @@ class NodeEditor:
356
356
  node = self.editor.node_manager.get_active_node()
357
357
  if node:
358
358
  node.properties = props
359
+ self.editor.suggestion_registry.refresh(self.editor)
359
360
 
360
361
  def _save_particle_config(self, cfg: dict):
361
362
  node = self.editor.node_manager.get_active_node()
362
363
  if node:
363
364
  node.properties.clear()
364
365
  node.properties.update(cfg)
366
+ self.editor.suggestion_registry.refresh(self.editor)
365
367
  self.editor.tile_grid_widget.reset_particle_preview(node.node_id, cfg)
366
368
 
367
369
  def draw(self, screen: pygame.Surface):