tilemap-editor 4.3.0__tar.gz → 4.3.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 (236) hide show
  1. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/PKG-INFO +10 -6
  2. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/README.md +7 -3
  3. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/pyproject.toml +3 -3
  4. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/editor.py +72 -74
  5. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/event_map.py +4 -3
  6. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/layers.py +28 -28
  7. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/node_manager.py +13 -13
  8. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/nodes.py +7 -7
  9. tilemap_editor-4.3.2/src/plugins/character_collision/editor.py +602 -0
  10. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/plugins/character_collision/models.py +29 -28
  11. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/plugins/character_collision/protocols.py +16 -16
  12. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/plugins/character_collision/shape_editor.py +50 -39
  13. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/plugins/character_collision/standalone.py +12 -5
  14. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/plugins/object_tileset_collision/__init__.py +1 -1
  15. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/plugins/object_tileset_collision/editor.py +102 -94
  16. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/plugins/object_tileset_collision/models.py +10 -10
  17. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/plugins/object_tileset_collision/protocols.py +2 -2
  18. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/plugins/object_tileset_collision/standalone.py +4 -1
  19. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/plugins/sprite_animation/__init__.py +2 -2
  20. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/plugins/sprite_animation/clipboard_util.py +0 -2
  21. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/plugins/sprite_animation/editor.py +205 -373
  22. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/plugins/sprite_animation/frame_picker.py +58 -83
  23. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/plugins/sprite_animation/models.py +15 -15
  24. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/plugins/sprite_animation/preview.py +47 -76
  25. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/plugins/sprite_animation/protocols.py +2 -2
  26. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/plugins/sprite_animation/runtime_load.py +16 -16
  27. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/plugins/sprite_animation/standalone.py +3 -2
  28. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/plugins/sprite_animation/timeline.py +55 -98
  29. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/plugins/sprite_animation/validation.py +4 -5
  30. tilemap_editor-4.3.2/src/plugins/sprite_editor/dialogs.py +197 -0
  31. tilemap_editor-4.3.2/src/plugins/sprite_editor/editor.py +631 -0
  32. tilemap_editor-4.3.2/src/plugins/sprite_editor/region_export.py +46 -0
  33. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/plugins/sprite_editor/standalone.py +12 -21
  34. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/plugins/tileset_collision/__init__.py +1 -1
  35. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/plugins/tileset_collision/collision_painter.py +57 -58
  36. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/plugins/tileset_collision/editor.py +94 -136
  37. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/plugins/tileset_collision/models.py +13 -13
  38. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/plugins/tileset_collision/protocols.py +3 -3
  39. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/plugins/tileset_collision/standalone.py +4 -2
  40. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/standalone_automap.py +5 -6
  41. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/standalone_error_console.py +37 -108
  42. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/standalone_filemanager.py +13 -10
  43. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/standalone_image_viewer.py +5 -6
  44. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap.py +14 -14
  45. tilemap_editor-4.3.2/src/tilemap_editor/assets/icons/check.svg +1 -0
  46. tilemap_editor-4.3.2/src/tilemap_editor/assets/icons/error.svg +1 -0
  47. tilemap_editor-4.3.2/src/tilemap_editor/assets/icons/fold_down_arrow.svg +1 -0
  48. tilemap_editor-4.3.2/src/tilemap_editor/assets/icons/gear.svg +1 -0
  49. tilemap_editor-4.3.2/src/tilemap_editor/assets/icons/info.svg +1 -0
  50. tilemap_editor-4.3.2/src/tilemap_editor/assets/icons/miniobj.svg +1 -0
  51. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/icons/tileset.svg +1 -1
  52. tilemap_editor-4.3.2/src/tilemap_editor/assets/icons/unfold_right_arrow.svg +1 -0
  53. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/cli.py +2 -1
  54. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/settings.py +2 -3
  55. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor.egg-info/PKG-INFO +10 -6
  56. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor.egg-info/SOURCES.txt +9 -1
  57. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor.egg-info/top_level.txt +0 -1
  58. tilemap_editor-4.3.2/src/ttypes/__init__.py +8 -0
  59. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/ttypes/tilemap.py +15 -15
  60. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/utils/__init__.py +1 -1
  61. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/utils/editor_preference.py +3 -3
  62. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/utils/error_handler.py +10 -10
  63. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/utils/font_manager.py +14 -16
  64. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/utils/history.py +3 -3
  65. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/utils/icon_manager.py +8 -8
  66. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/utils/icons_cache.py +31 -24
  67. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/utils/log_capture.py +3 -3
  68. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/utils/project_paths.py +2 -2
  69. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/utils/serialization.py +4 -4
  70. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/utils/standalone.py +19 -7
  71. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/utils/validation.py +1 -1
  72. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/automap_models.py +14 -15
  73. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/autotile_template.py +8 -8
  74. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/autotiler.py +47 -52
  75. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/filemanager.py +122 -217
  76. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/input.py +46 -29
  77. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/layer_selector.py +17 -16
  78. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/map_properties.py +3 -2
  79. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/mapsetup.py +5 -5
  80. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/particle_presets.py +6 -8
  81. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/particle_system.py +12 -14
  82. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/regex_automap_designer.py +20 -22
  83. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/spritesheet_grid.py +45 -57
  84. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/tile_grid.py +185 -286
  85. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/tile_selector.py +193 -280
  86. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/ui/button.py +5 -4
  87. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/ui/checkbox.py +3 -4
  88. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/ui/collision_layer_mask.py +3 -3
  89. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/ui/collision_layer_sidebar.py +9 -12
  90. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/ui/confirm_dialog.py +4 -3
  91. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/ui/dialog_base.py +5 -4
  92. tilemap_editor-4.3.2/src/widgets/ui/drag_tracker.py +145 -0
  93. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/ui/draw_utils.py +3 -3
  94. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/ui/fileinput.py +15 -21
  95. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/ui/label.py +0 -3
  96. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/ui/layer_type_dialog.py +5 -4
  97. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/ui/menubar.py +24 -27
  98. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/ui/mode_indicator.py +17 -19
  99. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/ui/node_editor.py +30 -31
  100. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/ui/node_selector.py +28 -28
  101. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/ui/notification.py +3 -3
  102. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/ui/particle_config_dialog.py +31 -32
  103. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/ui/property_editor.py +13 -15
  104. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/ui/region_selector.py +102 -121
  105. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/ui/scrollbar.py +16 -17
  106. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/ui/sidebar_container.py +7 -7
  107. tilemap_editor-4.3.2/src/widgets/ui/splitter.py +79 -0
  108. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/ui/status_bar.py +10 -10
  109. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/ui/theme.py +37 -16
  110. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/ui/tileset_type_dialog.py +23 -24
  111. tilemap_editor-4.3.2/src/widgets/ui/toast.py +245 -0
  112. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/ui/tool_manager.py +4 -5
  113. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/ui/toolbar.py +7 -9
  114. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/ui/tooltip.py +6 -5
  115. tilemap_editor-4.3.2/src/widgets/ui/tree_widget.py +452 -0
  116. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/tests/test_animation_library_grid_offset.py +1 -4
  117. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/tests/test_collision_layer_sidebar.py +13 -25
  118. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/tests/test_editor_pan_mode.py +1 -6
  119. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/tests/test_layers_autotile.py +9 -10
  120. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/tests/test_project_paths.py +2 -3
  121. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/tests/test_render_scale.py +1 -4
  122. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/tests/test_serialization_regression.py +3 -1
  123. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/tests/test_sprite_animation_editor_grid.py +23 -6
  124. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/tests/test_tile_selector_pick.py +1 -3
  125. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/tests/test_tilemap_save.py +1 -2
  126. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/tests/test_toolbar_tools.py +1 -2
  127. tilemap_editor-4.3.0/src/configs/themes.py +0 -81
  128. tilemap_editor-4.3.0/src/plugins/character_collision/editor.py +0 -425
  129. tilemap_editor-4.3.0/src/plugins/sprite_editor/editor.py +0 -521
  130. tilemap_editor-4.3.0/src/tilemap_editor/assets/icons/check.svg +0 -1
  131. tilemap_editor-4.3.0/src/tilemap_editor/assets/icons/error.svg +0 -1
  132. tilemap_editor-4.3.0/src/tilemap_editor/assets/icons/info.svg +0 -1
  133. tilemap_editor-4.3.0/src/ttypes/__init__.py +0 -6
  134. tilemap_editor-4.3.0/src/widgets/ui/drag_tracker.py +0 -62
  135. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/LICENSE +0 -0
  136. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/MANIFEST.in +0 -0
  137. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
  138. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
  139. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
  140. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
  141. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
  142. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
  143. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
  144. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
  145. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/arrow-down.svg +0 -0
  146. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/check.svg +0 -0
  147. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/checked.svg +0 -0
  148. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/close.svg +0 -0
  149. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/duplicate.svg +0 -0
  150. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/error.svg +0 -0
  151. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/file.svg +0 -0
  152. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/filedead.svg +0 -0
  153. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/fit.svg +0 -0
  154. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/folder.svg +0 -0
  155. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/grid.svg +0 -0
  156. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/image.svg +0 -0
  157. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/info.svg +0 -0
  158. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/load.svg +0 -0
  159. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/loop.svg +0 -0
  160. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/onion.svg +0 -0
  161. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/pan.svg +0 -0
  162. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/pause.svg +0 -0
  163. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/pencil.svg +0 -0
  164. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/play.svg +0 -0
  165. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/plus.svg +0 -0
  166. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/radio.svg +0 -0
  167. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/reset.svg +0 -0
  168. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/save.svg +0 -0
  169. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/skip-back.svg +0 -0
  170. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/skip-forward.svg +0 -0
  171. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/stop.svg +0 -0
  172. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/tilemap.svg +0 -0
  173. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/tileset.svg +0 -0
  174. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/unchecked.svg +0 -0
  175. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/warning.svg +0 -0
  176. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/zoomin.svg +0 -0
  177. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/assets/icons/zoomout.svg +0 -0
  178. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/setup.cfg +0 -0
  179. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/constants.py +0 -0
  180. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/main.py +0 -0
  181. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/plugins/__init__.py +0 -0
  182. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/plugins/character_collision/__init__.py +2 -2
  183. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/plugins/sprite_animation/__main__.py +0 -0
  184. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/plugins/sprite_editor/__init__.py +0 -0
  185. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/__init__.py +0 -0
  186. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/__main__.py +0 -0
  187. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/__init__.py +0 -0
  188. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
  189. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
  190. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
  191. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
  192. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
  193. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
  194. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
  195. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
  196. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/icons/ToolMove.svg +0 -0
  197. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/icons/arrow-down.svg +0 -0
  198. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/icons/checked.svg +0 -0
  199. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/icons/close.svg +0 -0
  200. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/icons/duplicate.svg +0 -0
  201. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/icons/file.svg +0 -0
  202. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/icons/filedead.svg +0 -0
  203. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/icons/fit.svg +0 -0
  204. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/icons/folder.svg +0 -0
  205. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/icons/grid.svg +0 -0
  206. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/icons/image.svg +0 -0
  207. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/icons/load.svg +0 -0
  208. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/icons/loop.svg +0 -0
  209. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/icons/onion.svg +0 -0
  210. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/icons/pan.svg +0 -0
  211. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/icons/pause.svg +0 -0
  212. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/icons/pencil.svg +0 -0
  213. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/icons/play.svg +0 -0
  214. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/icons/plus.svg +0 -0
  215. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/icons/radio.svg +0 -0
  216. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/icons/reset.svg +0 -0
  217. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/icons/save.svg +0 -0
  218. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/icons/skip-back.svg +0 -0
  219. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/icons/skip-forward.svg +0 -0
  220. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/icons/stop.svg +0 -0
  221. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/icons/tilemap.svg +0 -0
  222. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/icons/unchecked.svg +0 -0
  223. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/icons/warning.svg +0 -0
  224. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/icons/zoomin.svg +0 -0
  225. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/assets/icons/zoomout.svg +0 -0
  226. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor/main.py +0 -0
  227. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor.egg-info/dependency_links.txt +0 -0
  228. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor.egg-info/entry_points.txt +0 -0
  229. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/tilemap_editor.egg-info/requires.txt +0 -0
  230. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/__init__.py +0 -0
  231. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/src/widgets/widget_base.py +0 -0
  232. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/tests/test_collision_layer_mask.py +4 -4
  233. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/tests/test_drag_tracker.py +0 -0
  234. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/tests/test_gid_collision.py +2 -2
  235. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/tests/test_tile_grid_selection.py +3 -3
  236. {tilemap_editor-4.3.0 → tilemap_editor-4.3.2}/tests/test_timeline_invalidate_cache.py +0 -0
@@ -1,14 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tilemap-editor
3
- Version: 4.3.0
4
- Summary: Pygame tilemap editor with SVG icons, professional UI, and sprite animation tools
3
+ Version: 4.3.2
4
+ Summary: Pygame tilemap editor with collision, animation, sprite & particle tools
5
5
  Author: tilemap editor contributors
6
6
  License: Proprietary
7
7
  Classifier: Programming Language :: Python :: 3
8
8
  Classifier: Programming Language :: Python :: 3 :: Only
9
9
  Classifier: Topic :: Games/Entertainment
10
10
  Classifier: Topic :: Software Development
11
- Requires-Python: >=3.10
11
+ Requires-Python: >=3.11
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
14
  Requires-Dist: pygame-ce>=2.5
@@ -21,12 +21,18 @@ Dynamic: license-file
21
21
  ## Installation
22
22
 
23
23
  [tilemap-editor on PyPI](https://pypi.org/project/tilemap-editor/)
24
+ [tilemap-parser on deepwiki](https://deepwiki.com/FluffyBrudy/tilemap)
24
25
 
25
26
  ## Parser
26
27
 
27
28
  `tilemap-parser` is a utility to parse and display maps created by this editor. You can use it to load and visualize tilemaps in your game or application.
28
29
 
29
- [tilemap-parser on Vercel](https://tilemap-parser.vercel.app/)
30
+ [tilemap-parser on deepwiki](https://deepwiki.com/FluffyBrudy/tilemap-parser)
31
+ [tilemap-parser on PyPI](https://pypi.org/project/tilemap-parser/)
32
+
33
+ ## Kit
34
+
35
+ `pygkit` is general-purpose Pygame CE runtime kit: UI widgets, audio management, timers, interpolation, signals and utilities.
30
36
 
31
37
  ### Quick Start
32
38
 
@@ -42,7 +48,6 @@ tilemap-editor init
42
48
  tilemap-editor run
43
49
  ```
44
50
 
45
-
46
51
  ```python
47
52
  from editor import Editor
48
53
 
@@ -50,4 +55,3 @@ if __name__ == "__main__":
50
55
  editor = Editor()
51
56
  editor.run()
52
57
  ```
53
-
@@ -5,12 +5,18 @@
5
5
  ## Installation
6
6
 
7
7
  [tilemap-editor on PyPI](https://pypi.org/project/tilemap-editor/)
8
+ [tilemap-parser on deepwiki](https://deepwiki.com/FluffyBrudy/tilemap)
8
9
 
9
10
  ## Parser
10
11
 
11
12
  `tilemap-parser` is a utility to parse and display maps created by this editor. You can use it to load and visualize tilemaps in your game or application.
12
13
 
13
- [tilemap-parser on Vercel](https://tilemap-parser.vercel.app/)
14
+ [tilemap-parser on deepwiki](https://deepwiki.com/FluffyBrudy/tilemap-parser)
15
+ [tilemap-parser on PyPI](https://pypi.org/project/tilemap-parser/)
16
+
17
+ ## Kit
18
+
19
+ `pygkit` is general-purpose Pygame CE runtime kit: UI widgets, audio management, timers, interpolation, signals and utilities.
14
20
 
15
21
  ### Quick Start
16
22
 
@@ -26,7 +32,6 @@ tilemap-editor init
26
32
  tilemap-editor run
27
33
  ```
28
34
 
29
-
30
35
  ```python
31
36
  from editor import Editor
32
37
 
@@ -34,4 +39,3 @@ if __name__ == "__main__":
34
39
  editor = Editor()
35
40
  editor.run()
36
41
  ```
37
-
@@ -4,10 +4,10 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tilemap-editor"
7
- version = "4.3.0"
8
- description = "Pygame tilemap editor with SVG icons, professional UI, and sprite animation tools"
7
+ version = "4.3.2"
8
+ description = "Pygame tilemap editor with collision, animation, sprite & particle tools"
9
9
  readme = "README.md"
10
- requires-python = ">=3.10"
10
+ requires-python = ">=3.11"
11
11
  license = { text = "Proprietary" }
12
12
  authors = [{ name = "tilemap editor contributors" }]
13
13
  dependencies = [
@@ -5,13 +5,14 @@ import queue
5
5
  import subprocess
6
6
  import sys
7
7
  import threading
8
+ from collections.abc import Callable
8
9
  from pathlib import Path
9
- from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple
10
+ from typing import TYPE_CHECKING, Any
10
11
 
11
12
  import pygame
12
13
  from pygame import Rect
13
14
 
14
- from utils.font_manager import FontStyle, FontWeight, font_manager
15
+ from utils.font_manager import FontWeight, font_manager
15
16
 
16
17
  if sys.platform == "darwin":
17
18
  os.environ.setdefault("SDL_VIDEO_MAC_SCREEN_SCALE", "1")
@@ -20,7 +21,6 @@ from constants import BASE_PATH
20
21
  from node_manager import NodeManager
21
22
  from tilemap import Tilemap
22
23
  from utils import error_context, error_handler
23
- from utils.log_capture import setup_console_log
24
24
  from utils.standalone import launch_standalone
25
25
  from widgets.autotiler import AutotileRuleDesigner
26
26
  from widgets.layer_selector import LayerSelector
@@ -40,14 +40,13 @@ from widgets.ui.particle_config_dialog import ParticleConfigDialog
40
40
  from widgets.ui.property_editor import PropertyEditor
41
41
  from widgets.ui.sidebar_container import SidebarContainer, ToolbarAction
42
42
  from widgets.ui.theme import COLORS, THEMES, get_theme_manager, set_theme
43
- from widgets.ui.tool_manager import ToolKind, ToolManager
44
43
  from widgets.ui.tileset_type_dialog import TilesetTypeDialog
44
+ from widgets.ui.tool_manager import ToolKind, ToolManager
45
45
  from widgets.ui.toolbar import Toolbar
46
46
  from widgets.ui.tooltip import TooltipManager
47
47
 
48
48
  if TYPE_CHECKING:
49
49
  from plugins.sprite_animation import SpriteAnimationEditor
50
- from plugins.sprite_editor import SpriteEditor
51
50
 
52
51
 
53
52
  def setup_error_logging():
@@ -92,7 +91,7 @@ def _load_project_config() -> tuple[Path, Path, dict]:
92
91
  raise RuntimeError("settings.json not found. Run 'tilemap-editor init' first.")
93
92
 
94
93
  try:
95
- with open(settings_file, "r", encoding="utf-8") as f:
94
+ with open(settings_file, encoding="utf-8") as f:
96
95
  config = json.load(f)
97
96
  except json.JSONDecodeError as e:
98
97
  raise RuntimeError(f"Invalid settings.json: {e}")
@@ -102,6 +101,24 @@ def _load_project_config() -> tuple[Path, Path, dict]:
102
101
  if field not in config:
103
102
  raise RuntimeError(f"Invalid settings.json: missing '{field}'")
104
103
 
104
+ defaults = {
105
+ "theme": "dark",
106
+ "themes_list": ["dark", "molokai", "light", "semi_light"],
107
+ "nodes_path": "nodes",
108
+ "collision_paths": {"tileset": "collision", "character": "character_collision"},
109
+ }
110
+ changed = False
111
+ for key, val in defaults.items():
112
+ if key not in config:
113
+ config[key] = val
114
+ changed = True
115
+ if changed:
116
+ try:
117
+ with open(settings_file, "w", encoding="utf-8") as f:
118
+ json.dump(config, f, indent=4)
119
+ except Exception:
120
+ pass
121
+
105
122
  base_path = Path(config["base_path"]).expanduser()
106
123
 
107
124
  if not base_path.is_absolute():
@@ -141,9 +158,9 @@ _launch_standalone_module = launch_standalone
141
158
  class Editor:
142
159
  def __init__(
143
160
  self,
144
- size: Optional[Tuple[int, int]] = None,
161
+ size: tuple[int, int] | None = None,
145
162
  fps=60,
146
- theme: Optional[str] = None,
163
+ theme: str | None = None,
147
164
  ):
148
165
  self.base_path, self.data_root, self.config = _load_project_config()
149
166
  logger.info(f"Project base_path: {self.base_path}")
@@ -181,27 +198,27 @@ class Editor:
181
198
  self.selector_w = 300
182
199
  self._tileset_dragging = False
183
200
  self.left_panel_w = 380
184
- self.map_setup_widget: Optional[MapSetup] = None
185
- self.map_properties_dialog: Optional[MapPropertiesDialog] = None
186
- self.tileset_widget: Optional[TileSelector] = None
187
- self.layer_widget: Optional[LayerSelector] = None
188
- self.tile_grid_widget: Optional[TileGrid] = None
201
+ self.map_setup_widget: MapSetup | None = None
202
+ self.map_properties_dialog: MapPropertiesDialog | None = None
203
+ self.tileset_widget: TileSelector | None = None
204
+ self.layer_widget: LayerSelector | None = None
205
+ self.tile_grid_widget: TileGrid | None = None
189
206
 
190
207
  self.left_panel_visible = False
191
- self.animation_panel: Optional["SpriteAnimationEditor"] = None
192
- self._animation_panel_surface: Optional[pygame.Surface] = None
208
+ self.animation_panel: SpriteAnimationEditor | None = None
209
+ self._animation_panel_surface: pygame.Surface | None = None
193
210
 
194
211
  self.autotiler = AutotileRuleDesigner(self, 100, 100)
195
212
  self.regex_automap_designer = RegexAutomapDesigner(self, 150, 100)
196
213
  self.notifications = NotificationManager(self)
197
214
  self.tooltip = TooltipManager()
198
- self.error_console_process: Optional[subprocess.Popen] = None
199
- self.property_editor: Optional[PropertyEditor] = None
200
- self.particle_config_dialog: Optional[ParticleConfigDialog] = None
215
+ self.error_console_process: subprocess.Popen | None = None
216
+ self.property_editor: PropertyEditor | None = None
217
+ self.particle_config_dialog: ParticleConfigDialog | None = None
201
218
 
202
- self.child_processes: List[subprocess.Popen] = []
203
- self.file_manager_process: Optional[subprocess.Popen] = None
204
- self._file_manager_callbacks: Dict[str, Any] = {}
219
+ self.child_processes: list[subprocess.Popen] = []
220
+ self.file_manager_process: subprocess.Popen | None = None
221
+ self._file_manager_callbacks: dict[str, Any] = {}
205
222
 
206
223
  self.loading_state = {
207
224
  "active": False,
@@ -209,7 +226,7 @@ class Editor:
209
226
  "error": None,
210
227
  "path": None,
211
228
  }
212
- self._load_queue: "queue.Queue" = queue.Queue()
229
+ self._load_queue: queue.Queue = queue.Queue()
213
230
 
214
231
  editor_rect = self.screen.get_rect()
215
232
  editor_rect.center = ((width - 300) // 2, height // 2)
@@ -265,14 +282,16 @@ class Editor:
265
282
  def open_file_manager(
266
283
  self,
267
284
  on_select: Callable[[Path], None] = lambda p: None,
268
- initial_dir: Optional[Path] = None,
269
- allowed_exts: List[str] = [".png", ".jpg", ".json"],
285
+ initial_dir: Path | None = None,
286
+ allowed_exts: list[str] = None,
270
287
  mode: str = "open",
271
- on_save: Optional[Callable[[Path], None]] = None,
288
+ on_save: Callable[[Path], None] | None = None,
272
289
  default_name: str = "",
273
290
  multi_select: bool = False,
274
291
  ):
275
292
  """Launch file manager as a subprocess."""
293
+ if allowed_exts is None:
294
+ allowed_exts = [".png", ".jpg", ".json"]
276
295
  if self.file_manager_process and self.file_manager_process.poll() is None:
277
296
  return
278
297
 
@@ -718,13 +737,14 @@ class Editor:
718
737
  new_theme = theme_names[next_idx]
719
738
  set_theme(new_theme)
720
739
  from utils.icon_manager import icon_manager
740
+
721
741
  icon_manager.clear_cache()
722
742
  self.notifications.notify(f"Theme: {new_theme}")
723
743
 
724
744
  settings_file = Path.cwd() / "settings.json"
725
745
  if settings_file.exists():
726
746
  try:
727
- with open(settings_file, "r", encoding="utf-8") as f:
747
+ with open(settings_file, encoding="utf-8") as f:
728
748
  config = json.load(f)
729
749
  config["theme"] = new_theme
730
750
  with open(settings_file, "w", encoding="utf-8") as f:
@@ -736,13 +756,13 @@ class Editor:
736
756
  if hasattr(self.autotiler, "_launch_external_viewer"):
737
757
  self.autotiler._launch_external_viewer()
738
758
 
739
- def _active_tileset_image_path(self) -> Optional[Path]:
759
+ def _active_tileset_image_path(self) -> Path | None:
740
760
  """Filesystem path of the tileset image to use for the animation editor."""
741
761
  tw = self.tileset_widget
742
762
  if not tw or not tw.tilesets:
743
763
  return None
744
764
 
745
- candidates: List[Path] = []
765
+ candidates: list[Path] = []
746
766
  if 0 <= tw.active_idx < len(tw.tilesets):
747
767
  candidates.append(tw.tilesets[tw.active_idx].path)
748
768
  candidates.append(tw.tilesets[0].path)
@@ -925,7 +945,7 @@ class Editor:
925
945
  if logger:
926
946
  logger.info(msg)
927
947
 
928
- def _write_propagation_groups(self, tileset_path: Path) -> Optional[Path]:
948
+ def _write_propagation_groups(self, tileset_path: Path) -> Path | None:
929
949
  """Collect auto-tile variant groups for a tileset and write to temp JSON.
930
950
 
931
951
  Returns the path to the temp file, or None if no autotiler data is available.
@@ -950,7 +970,7 @@ class Editor:
950
970
  if tileset_index is None:
951
971
  return None
952
972
 
953
- groups: Dict[str, List[int]] = {}
973
+ groups: dict[str, list[int]] = {}
954
974
  for group in self.autotiler.groups:
955
975
  for rule in group.rules:
956
976
  if rule.tileset_index == tileset_index and rule.variant_ids:
@@ -1003,37 +1023,15 @@ class Editor:
1003
1023
  logger.info(msg)
1004
1024
 
1005
1025
  def launch_character_collision_editor(self):
1006
- """Launch the character collision editor in a new window.
1007
-
1008
- Opens a file picker to select a character sprite image.
1009
- """
1010
- self.open_file_manager(
1011
- on_select=self._launch_character_collision_editor_with_image,
1012
- initial_dir=self.data_root,
1013
- allowed_exts=[".png", ".jpg", ".jpeg"],
1014
- mode="open",
1015
- )
1016
-
1017
- def _launch_character_collision_editor_with_image(self, path: Path):
1018
- """Launch character collision editor subprocess with selected image."""
1026
+ """Launch the character collision editor in a new window."""
1019
1027
  try:
1020
- character_name = path.stem
1021
-
1022
1028
  args = [
1023
- str(path),
1024
1029
  "--name",
1025
- character_name,
1030
+ "Character",
1026
1031
  "--data-root",
1027
1032
  str(self.data_root),
1028
1033
  ]
1029
1034
 
1030
- collision_dir = self.data_root / self.config.get("collision_paths", {}).get(
1031
- "character", "character_collision"
1032
- )
1033
- collision_path = collision_dir / f"{character_name}.collision.json"
1034
- if collision_path.exists():
1035
- args.extend(["--load", str(collision_path)])
1036
-
1037
1035
  process = launch_standalone(
1038
1036
  "plugins.character_collision.standalone",
1039
1037
  args,
@@ -1042,7 +1040,7 @@ class Editor:
1042
1040
  )
1043
1041
  self.child_processes.append(process)
1044
1042
 
1045
- print(f"Launched character collision editor with: {path.name} (character: {character_name})")
1043
+ print("Launched character collision editor")
1046
1044
  except Exception as e:
1047
1045
  error_handler.capture(e, context="launch_character_collision_editor")
1048
1046
 
@@ -1181,40 +1179,40 @@ class Editor:
1181
1179
  if event.key == pygame.K_r and (ctrl_held or meta_held):
1182
1180
  self.toggle_autotiler()
1183
1181
  continue
1184
- elif event.key == pygame.K_m and (ctrl_held or meta_held):
1182
+ if event.key == pygame.K_m and (ctrl_held or meta_held):
1185
1183
  self.toggle_regex_automap()
1186
1184
  continue
1187
- elif event.key == pygame.K_s and (ctrl_held or meta_held):
1185
+ if event.key == pygame.K_s and (ctrl_held or meta_held):
1188
1186
  if shift_held:
1189
1187
  self.open_save_as_dialog()
1190
1188
  else:
1191
1189
  self.perform_quick_save()
1192
1190
  continue
1193
- elif event.key == pygame.K_BACKQUOTE and (ctrl_held or meta_held):
1191
+ if event.key == pygame.K_BACKQUOTE and (ctrl_held or meta_held):
1194
1192
  self.launch_error_console()
1195
1193
  continue
1196
- elif event.key == pygame.K_z and (ctrl_held or meta_held):
1194
+ if event.key == pygame.K_z and (ctrl_held or meta_held):
1197
1195
  if shift_held:
1198
1196
  self.tilemap.redo()
1199
1197
  else:
1200
1198
  self.tilemap.undo()
1201
1199
  continue
1202
- elif event.key == pygame.K_y and (ctrl_held or meta_held):
1200
+ if event.key == pygame.K_y and (ctrl_held or meta_held):
1203
1201
  self.tilemap.redo()
1204
1202
  continue
1205
- elif event.key == pygame.K_n and (ctrl_held or meta_held) and shift_held:
1203
+ if event.key == pygame.K_n and (ctrl_held or meta_held) and shift_held:
1206
1204
  self.node_editing_mode = not self.node_editing_mode
1207
1205
  if self.node_editing_mode:
1208
1206
  self.show_nodes = False
1209
1207
  self.tool_manager.deactivate()
1210
1208
  continue
1211
- elif event.key == pygame.K_n and (ctrl_held or meta_held):
1209
+ if event.key == pygame.K_n and (ctrl_held or meta_held):
1212
1210
  self.open_map_setup()
1213
1211
  continue
1214
- elif event.key == pygame.K_o and (ctrl_held or meta_held):
1212
+ if event.key == pygame.K_o and (ctrl_held or meta_held):
1215
1213
  self.perform_load()
1216
1214
  continue
1217
- elif event.key == pygame.K_SPACE and (ctrl_held or meta_held):
1215
+ if event.key == pygame.K_SPACE and (ctrl_held or meta_held):
1218
1216
  if self.tool_manager.is_active(ToolKind.PAN):
1219
1217
  restored = self.tool_manager.restore_previous()
1220
1218
  if restored is None and self._prev_tool == "nodes":
@@ -1232,19 +1230,19 @@ class Editor:
1232
1230
  self.node_editing_mode = False
1233
1231
  self.tool_manager.activate(ToolKind.PAN)
1234
1232
  continue
1235
- elif event.key == pygame.K_g and (ctrl_held or meta_held):
1233
+ if event.key == pygame.K_g and (ctrl_held or meta_held):
1236
1234
  self.toggle_grid()
1237
1235
  continue
1238
- elif event.key == pygame.K_e and (ctrl_held or meta_held) and shift_held:
1236
+ if event.key == pygame.K_e and (ctrl_held or meta_held) and shift_held:
1239
1237
  self.export_selection_as_png()
1240
1238
  continue
1241
- elif event.key == pygame.K_t and (ctrl_held or meta_held):
1239
+ if event.key == pygame.K_t and (ctrl_held or meta_held):
1242
1240
  self.cycle_theme()
1243
1241
  continue
1244
- elif event.key == pygame.K_b and (ctrl_held or meta_held):
1242
+ if event.key == pygame.K_b and (ctrl_held or meta_held):
1245
1243
  self.toggle_animation_panel()
1246
1244
  continue
1247
- elif pygame.K_1 <= event.key <= pygame.K_9:
1245
+ if pygame.K_1 <= event.key <= pygame.K_9:
1248
1246
  if not (self.node_editor and self.node_editor.visible and self.node_editor.editing_field):
1249
1247
  idx = event.key - pygame.K_1
1250
1248
  if idx < self.tilemap.layer_manager.get_layer_count():
@@ -1314,7 +1312,7 @@ class Editor:
1314
1312
  if hasattr(self.animation_panel, "update"):
1315
1313
  self.animation_panel.update()
1316
1314
 
1317
- self.screen.fill((30, 30, 30))
1315
+ self.screen.fill(COLORS.bg)
1318
1316
  self.tooltip.hide()
1319
1317
 
1320
1318
  if self.tile_grid_widget:
@@ -1343,12 +1341,12 @@ class Editor:
1343
1341
 
1344
1342
  if self.left_panel_visible and self.animation_panel:
1345
1343
  panel_surf = pygame.Surface((self.left_panel_w, self.height), pygame.SRCALPHA)
1346
- panel_surf.fill((28, 30, 34, 255))
1344
+ panel_surf.fill((*COLORS.panel_alt, 255))
1347
1345
  self.screen.blit(panel_surf, (0, 65))
1348
1346
 
1349
1347
  pygame.draw.rect(
1350
1348
  self.screen,
1351
- (60, 62, 65),
1349
+ COLORS.border,
1352
1350
  Rect(0, 65, self.left_panel_w, self.height - 65),
1353
1351
  1,
1354
1352
  )
@@ -1391,13 +1389,13 @@ class Editor:
1391
1389
 
1392
1390
  msg = self.loading_state.get("message", "Loading...")
1393
1391
  font = font_manager.get_font("noto", 18, FontWeight.BOLD)
1394
- text = font.render(msg, True, (230, 230, 230))
1392
+ text = font.render(msg, True, COLORS.text)
1395
1393
  text_rect = text.get_rect(center=(self.width // 2, self.height // 2))
1396
1394
  self.screen.blit(text, text_rect)
1397
1395
 
1398
1396
  dot_font = font_manager.get_font("noto", 16, FontWeight.REGULAR)
1399
1397
  dots = "." * ((pygame.time.get_ticks() // 400) % 4)
1400
- dot_surf = dot_font.render(dots, True, (180, 180, 180))
1398
+ dot_surf = dot_font.render(dots, True, COLORS.text_muted)
1401
1399
  dot_rect = dot_surf.get_rect(center=(self.width // 2, self.height // 2 + 30))
1402
1400
  self.screen.blit(dot_surf, dot_rect)
1403
1401
 
@@ -1,4 +1,5 @@
1
- from typing import TYPE_CHECKING, Callable, Dict, Tuple
1
+ from collections.abc import Callable
2
+ from typing import TYPE_CHECKING
2
3
 
3
4
  from pygame import QUIT, Event
4
5
 
@@ -10,11 +11,11 @@ class EventMap:
10
11
  def __init__(self, editor: "Editor") -> None:
11
12
  self.editor = editor
12
13
 
13
- self.event_map: Dict[Tuple[int, int | None], Callable[[Event], None]] = {
14
+ self.event_map: dict[tuple[int, int | None], Callable[[Event], None]] = {
14
15
  (QUIT, None): lambda _: self.quit()
15
16
  }
16
17
 
17
- def get_event(self, event_key: Tuple[int, int | None]):
18
+ def get_event(self, event_key: tuple[int, int | None]):
18
19
  return self.event_map.get(event_key, None)
19
20
 
20
21
  def quit(self):
@@ -4,7 +4,7 @@ Supports multiple layers with independent tile and object data.
4
4
  """
5
5
 
6
6
  import random
7
- from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Tuple
7
+ from typing import TYPE_CHECKING, Any
8
8
 
9
9
  from ttypes.tilemap import TypeObject, TypeTile
10
10
 
@@ -34,11 +34,11 @@ class Layer:
34
34
  self.visible = visible
35
35
  self.locked = locked
36
36
  self.opacity = max(0.0, min(1.0, opacity))
37
- self.properties: Dict[str, Any] = {}
37
+ self.properties: dict[str, Any] = {}
38
38
 
39
- self.tiles: Dict[Tuple[int, int], TypeTile] = {}
39
+ self.tiles: dict[tuple[int, int], TypeTile] = {}
40
40
 
41
- self.objects: Dict[int, TypeObject] = {}
41
+ self.objects: dict[int, TypeObject] = {}
42
42
  self.next_object_id: int = 1
43
43
 
44
44
  self._autotile_cache = {
@@ -48,20 +48,20 @@ class Layer:
48
48
  "significant_offsets": set(),
49
49
  }
50
50
 
51
- def set_tile(self, pos: Tuple[int, int], tile: TypeTile) -> None:
51
+ def set_tile(self, pos: tuple[int, int], tile: TypeTile) -> None:
52
52
  """Set a tile at the given grid position."""
53
53
  if not self.locked:
54
54
  self.tiles[pos] = tile
55
55
 
56
- def get_tile(self, pos: Tuple[int, int]) -> Optional[TypeTile]:
56
+ def get_tile(self, pos: tuple[int, int]) -> TypeTile | None:
57
57
  """Get a tile at the given grid position."""
58
58
  return self.tiles.get(pos)
59
59
 
60
- def autotile_layer(self, rules: List["AutotileRule"]) -> int:
60
+ def autotile_layer(self, rules: list["AutotileRule"]) -> int:
61
61
  """Update all tiles in this layer according to autotile rules."""
62
62
  return self._autotile_tiles(rules, list(self.tiles.keys()))
63
63
 
64
- def autotile_at_pos(self, pos: Tuple[int, int], rules: List["AutotileRule"]) -> int:
64
+ def autotile_at_pos(self, pos: tuple[int, int], rules: list["AutotileRule"]) -> int:
65
65
  """Update the tile at pos and its 8 neighbors according to autotile rules."""
66
66
  positions = [pos]
67
67
  for dx, dy in [
@@ -79,7 +79,7 @@ class Layer:
79
79
  existing_positions = [p for p in positions if p in self.tiles]
80
80
  return self._autotile_tiles(rules, existing_positions)
81
81
 
82
- def _autotile_tiles(self, rules: List["AutotileRule"], positions: List[Tuple[int, int]]) -> int:
82
+ def _autotile_tiles(self, rules: list["AutotileRule"], positions: list[tuple[int, int]]) -> int:
83
83
  """
84
84
  Internal helper to update specific tiles according to autotile rules.
85
85
 
@@ -99,9 +99,9 @@ class Layer:
99
99
  rules_hash = hash(tuple((r.group_id, r.tileset_index, tuple(sorted(r.variant_ids))) for r in rules))
100
100
 
101
101
  if self._autotile_cache["rules_hash"] != rules_hash:
102
- variant_to_group: Dict[Tuple[int, int], str] = {}
103
- rules_by_group: Dict[str, List["AutotileRule"]] = {}
104
- significant_offsets: Set[Tuple[int, int]] = set()
102
+ variant_to_group: dict[tuple[int, int], str] = {}
103
+ rules_by_group: dict[str, list[AutotileRule]] = {}
104
+ significant_offsets: set[tuple[int, int]] = set()
105
105
 
106
106
  for rule in rules:
107
107
  gid = rule.group_id
@@ -133,7 +133,7 @@ class Layer:
133
133
 
134
134
  changes_count = 0
135
135
 
136
- def _get_group(t: dict) -> Optional[str]:
136
+ def _get_group(t: dict) -> str | None:
137
137
  """Get group_id for a tile: autotile_group field > legacy variant lookup."""
138
138
  ag = t.get("autotile_group")
139
139
  if ag is not None:
@@ -176,8 +176,8 @@ class Layer:
176
176
 
177
177
  neighbor_offsets_set = {n for n in actual_neighbors if n in significant_offsets}
178
178
 
179
- matched_rule: Optional["AutotileRule"] = None
180
- new_variant: Optional[int] = None
179
+ matched_rule: AutotileRule | None = None
180
+ new_variant: int | None = None
181
181
  for rule in group_rules:
182
182
  if rule.neighbors == neighbor_offsets_set and rule.tileset_index == ttype:
183
183
  matched_rule = rule
@@ -201,7 +201,7 @@ class Layer:
201
201
 
202
202
  return changes_count
203
203
 
204
- def remove_tile(self, pos: Tuple[int, int]) -> bool:
204
+ def remove_tile(self, pos: tuple[int, int]) -> bool:
205
205
  """Remove a tile at the given grid position. Returns True if tile existed."""
206
206
  if not self.locked and pos in self.tiles:
207
207
  del self.tiles[pos]
@@ -210,9 +210,9 @@ class Layer:
210
210
 
211
211
  def flood_fill(
212
212
  self,
213
- start_pos: Tuple[int, int],
213
+ start_pos: tuple[int, int],
214
214
  new_tile_data: TypeTile,
215
- map_size: Tuple[int, int],
215
+ map_size: tuple[int, int],
216
216
  ) -> None:
217
217
  """Replace contiguous tiles of the same type starting from start_pos."""
218
218
  if self.locked or self.layer_type != "tile":
@@ -252,11 +252,11 @@ class Layer:
252
252
  seen.add(next_pos)
253
253
  queue.append(next_pos)
254
254
 
255
- def get_all_tiles(self) -> Dict[Tuple[int, int], TypeTile]:
255
+ def get_all_tiles(self) -> dict[tuple[int, int], TypeTile]:
256
256
  """Return a copy of all tiles in this layer."""
257
257
  return dict(self.tiles)
258
258
 
259
- def add_object(self, pos: Tuple[int, int], obj: TypeObject) -> int:
259
+ def add_object(self, pos: tuple[int, int], obj: TypeObject) -> int:
260
260
  """Add an object at the given pixel position. Returns the object ID."""
261
261
  if not self.locked:
262
262
  obj_id = self.next_object_id
@@ -265,7 +265,7 @@ class Layer:
265
265
  return obj_id
266
266
  return -1
267
267
 
268
- def get_object(self, obj_id: int) -> Optional[TypeObject]:
268
+ def get_object(self, obj_id: int) -> TypeObject | None:
269
269
  """Get an object by ID."""
270
270
  return self.objects.get(obj_id)
271
271
 
@@ -276,11 +276,11 @@ class Layer:
276
276
  return True
277
277
  return False
278
278
 
279
- def get_all_objects(self) -> Dict[int, TypeObject]:
279
+ def get_all_objects(self) -> dict[int, TypeObject]:
280
280
  """Return a copy of all objects in this layer."""
281
281
  return dict(self.objects)
282
282
 
283
- def move_object(self, obj_id: int, new_pos: Tuple[int, int]) -> bool:
283
+ def move_object(self, obj_id: int, new_pos: tuple[int, int]) -> bool:
284
284
  """Move an object to a new position. Returns True if successful."""
285
285
  if not self.locked and obj_id in self.objects:
286
286
  self.objects[obj_id]["area"]["x"] = new_pos[0]
@@ -364,14 +364,14 @@ class LayerManager:
364
364
  """Manages multiple layers for a tilemap."""
365
365
 
366
366
  def __init__(self):
367
- self.layers: List[Layer] = []
367
+ self.layers: list[Layer] = []
368
368
  self.active_layer_idx: int = -1
369
369
 
370
370
  def create_layer(
371
371
  self,
372
372
  name: str,
373
373
  layer_type: str = "tile",
374
- insert_index: Optional[int] = None,
374
+ insert_index: int | None = None,
375
375
  ) -> Layer:
376
376
  """Create a new layer and add it to the manager."""
377
377
  z_index = len(self.layers)
@@ -402,13 +402,13 @@ class LayerManager:
402
402
 
403
403
  return True
404
404
 
405
- def get_layer(self, index: int) -> Optional[Layer]:
405
+ def get_layer(self, index: int) -> Layer | None:
406
406
  """Get a layer by index."""
407
407
  if 0 <= index < len(self.layers):
408
408
  return self.layers[index]
409
409
  return None
410
410
 
411
- def get_active_layer(self) -> Optional[Layer]:
411
+ def get_active_layer(self) -> Layer | None:
412
412
  """Get the currently active layer."""
413
413
  return self.get_layer(self.active_layer_idx)
414
414
 
@@ -432,7 +432,7 @@ class LayerManager:
432
432
  return True
433
433
  return False
434
434
 
435
- def get_rendered_layers(self) -> List[Layer]:
435
+ def get_rendered_layers(self) -> list[Layer]:
436
436
  """Get all visible layers sorted by z_index for rendering."""
437
437
  visible = [layer for layer in self.layers if layer.visible]
438
438
  return sorted(visible, key=lambda l: l.z_index)