tilemap-editor 3.3.10__tar.gz → 4.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. {tilemap_editor-3.3.10/src/tilemap_editor.egg-info → tilemap_editor-4.0.0}/PKG-INFO +1 -1
  2. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/pyproject.toml +1 -1
  3. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/editor.py +104 -52
  4. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/sprite_animation/preview.py +11 -64
  5. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/tileset_collision/editor.py +1 -1
  6. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap.py +13 -12
  7. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/settings.py +2 -0
  8. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0/src/tilemap_editor.egg-info}/PKG-INFO +1 -1
  9. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor.egg-info/SOURCES.txt +7 -0
  10. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/utils/serialization.py +12 -6
  11. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/filemanager.py +85 -186
  12. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/input.py +198 -92
  13. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/layer_selector.py +40 -55
  14. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/map_properties.py +39 -44
  15. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/mapsetup.py +72 -56
  16. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/tile_grid.py +361 -165
  17. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/tile_selector.py +46 -117
  18. tilemap_editor-4.0.0/src/widgets/ui/button.py +83 -0
  19. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/confirm_dialog.py +19 -46
  20. tilemap_editor-4.0.0/src/widgets/ui/dialog_base.py +70 -0
  21. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/draw_utils.py +11 -0
  22. tilemap_editor-4.0.0/src/widgets/ui/label.py +40 -0
  23. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/layer_type_dialog.py +23 -50
  24. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/property_editor.py +116 -84
  25. tilemap_editor-4.0.0/src/widgets/ui/scrollbar.py +144 -0
  26. tilemap_editor-4.0.0/src/widgets/ui/sidebar_container.py +155 -0
  27. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/theme.py +9 -0
  28. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/tileset_type_dialog.py +35 -62
  29. tilemap_editor-4.0.0/src/widgets/widget_base.py +42 -0
  30. tilemap_editor-4.0.0/tests/test_serialization_regression.py +87 -0
  31. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/LICENSE +0 -0
  32. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/MANIFEST.in +0 -0
  33. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/README.md +0 -0
  34. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
  35. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
  36. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
  37. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
  38. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
  39. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
  40. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
  41. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
  42. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/arrow-down.svg +0 -0
  43. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/check.svg +0 -0
  44. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/checked.svg +0 -0
  45. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/close.svg +0 -0
  46. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/duplicate.svg +0 -0
  47. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/error.svg +0 -0
  48. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/file.svg +0 -0
  49. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/filedead.svg +0 -0
  50. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/fit.svg +0 -0
  51. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/folder.svg +0 -0
  52. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/grid.svg +0 -0
  53. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/image.svg +0 -0
  54. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/info.svg +0 -0
  55. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/load.svg +0 -0
  56. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/loop.svg +0 -0
  57. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/onion.svg +0 -0
  58. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/pan.svg +0 -0
  59. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/pause.svg +0 -0
  60. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/pencil.svg +0 -0
  61. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/play.svg +0 -0
  62. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/plus.svg +0 -0
  63. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/radio.svg +0 -0
  64. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/reset.svg +0 -0
  65. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/save.svg +0 -0
  66. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/skip-back.svg +0 -0
  67. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/skip-forward.svg +0 -0
  68. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/stop.svg +0 -0
  69. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/tilemap.svg +0 -0
  70. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/tileset.svg +0 -0
  71. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/unchecked.svg +0 -0
  72. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/warning.svg +0 -0
  73. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/zoomin.svg +0 -0
  74. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/assets/icons/zoomout.svg +0 -0
  75. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/setup.cfg +0 -0
  76. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/configs/themes.py +0 -0
  77. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/constants.py +0 -0
  78. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/event_map.py +0 -0
  79. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/layers.py +0 -0
  80. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/main.py +0 -0
  81. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/node_manager.py +0 -0
  82. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/nodes.py +0 -0
  83. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/__init__.py +0 -0
  84. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/character_collision/__init__.py +0 -0
  85. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/character_collision/editor.py +0 -0
  86. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/character_collision/models.py +0 -0
  87. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/character_collision/protocols.py +0 -0
  88. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/character_collision/shape_editor.py +0 -0
  89. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/character_collision/standalone.py +0 -0
  90. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/object_tileset_collision/__init__.py +0 -0
  91. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/object_tileset_collision/editor.py +0 -0
  92. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/object_tileset_collision/models.py +0 -0
  93. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/object_tileset_collision/protocols.py +0 -0
  94. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/object_tileset_collision/standalone.py +0 -0
  95. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/sprite_animation/__init__.py +0 -0
  96. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/sprite_animation/__main__.py +0 -0
  97. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/sprite_animation/clipboard_util.py +0 -0
  98. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/sprite_animation/editor.py +0 -0
  99. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/sprite_animation/frame_picker.py +0 -0
  100. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/sprite_animation/models.py +0 -0
  101. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/sprite_animation/protocols.py +0 -0
  102. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/sprite_animation/runtime_load.py +0 -0
  103. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/sprite_animation/standalone.py +0 -0
  104. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/sprite_animation/timeline.py +0 -0
  105. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/sprite_animation/validation.py +0 -0
  106. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/sprite_editor/__init__.py +0 -0
  107. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/sprite_editor/editor.py +0 -0
  108. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/sprite_editor/standalone.py +0 -0
  109. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/tileset_collision/__init__.py +0 -0
  110. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/tileset_collision/collision_painter.py +0 -0
  111. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/tileset_collision/models.py +0 -0
  112. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/tileset_collision/protocols.py +0 -0
  113. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/plugins/tileset_collision/standalone.py +0 -0
  114. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/standalone_automap.py +0 -0
  115. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/standalone_error_console.py +0 -0
  116. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/standalone_filemanager.py +0 -0
  117. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/standalone_image_viewer.py +0 -0
  118. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/__init__.py +0 -0
  119. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/__main__.py +0 -0
  120. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/__init__.py +0 -0
  121. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
  122. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
  123. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
  124. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
  125. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
  126. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
  127. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
  128. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
  129. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/ToolMove.svg +0 -0
  130. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/arrow-down.svg +0 -0
  131. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/check.svg +0 -0
  132. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/checked.svg +0 -0
  133. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/close.svg +0 -0
  134. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/duplicate.svg +0 -0
  135. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/error.svg +0 -0
  136. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/file.svg +0 -0
  137. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/filedead.svg +0 -0
  138. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/fit.svg +0 -0
  139. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/folder.svg +0 -0
  140. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/grid.svg +0 -0
  141. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/image.svg +0 -0
  142. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/info.svg +0 -0
  143. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/load.svg +0 -0
  144. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/loop.svg +0 -0
  145. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/onion.svg +0 -0
  146. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/pan.svg +0 -0
  147. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/pause.svg +0 -0
  148. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/pencil.svg +0 -0
  149. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/play.svg +0 -0
  150. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/plus.svg +0 -0
  151. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/radio.svg +0 -0
  152. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/reset.svg +0 -0
  153. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/save.svg +0 -0
  154. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/skip-back.svg +0 -0
  155. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/skip-forward.svg +0 -0
  156. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/stop.svg +0 -0
  157. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/tilemap.svg +0 -0
  158. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/tileset.svg +0 -0
  159. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/unchecked.svg +0 -0
  160. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/warning.svg +0 -0
  161. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/zoomin.svg +0 -0
  162. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/assets/icons/zoomout.svg +0 -0
  163. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/cli.py +0 -0
  164. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor/main.py +0 -0
  165. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor.egg-info/dependency_links.txt +0 -0
  166. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor.egg-info/entry_points.txt +0 -0
  167. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor.egg-info/requires.txt +0 -0
  168. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/tilemap_editor.egg-info/top_level.txt +0 -0
  169. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/ttypes/__init__.py +0 -0
  170. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/ttypes/tilemap.py +0 -0
  171. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/utils/__init__.py +0 -0
  172. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/utils/editor_preference.py +0 -0
  173. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/utils/error_handler.py +0 -0
  174. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/utils/font_manager.py +0 -0
  175. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/utils/history.py +0 -0
  176. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/utils/icon_manager.py +0 -0
  177. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/utils/icons_cache.py +0 -0
  178. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/utils/log_capture.py +0 -0
  179. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/utils/project_paths.py +0 -0
  180. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/utils/standalone.py +0 -0
  181. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/utils/validation.py +0 -0
  182. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/__init__.py +0 -0
  183. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/automap_models.py +0 -0
  184. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/autotile_template.py +0 -0
  185. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/autotiler.py +0 -0
  186. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/particle_presets.py +0 -0
  187. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/particle_system.py +0 -0
  188. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/regex_automap_designer.py +0 -0
  189. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/spritesheet_grid.py +0 -0
  190. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/checkbox.py +0 -0
  191. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/collision_layer_mask.py +0 -0
  192. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/collision_layer_sidebar.py +0 -0
  193. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/fileinput.py +0 -0
  194. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/menubar.py +0 -0
  195. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/mode_indicator.py +0 -0
  196. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/node_editor.py +0 -0
  197. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/node_selector.py +0 -0
  198. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/notification.py +0 -0
  199. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/particle_config_dialog.py +0 -0
  200. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/region_selector.py +0 -0
  201. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/status_bar.py +0 -0
  202. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/toolbar.py +0 -0
  203. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/src/widgets/ui/tooltip.py +0 -0
  204. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/tests/test_animation_library_grid_offset.py +0 -0
  205. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/tests/test_collision_layer_mask.py +0 -0
  206. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/tests/test_collision_layer_sidebar.py +0 -0
  207. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/tests/test_editor_pan_mode.py +0 -0
  208. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/tests/test_project_paths.py +0 -0
  209. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/tests/test_render_scale.py +0 -0
  210. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/tests/test_sprite_animation_editor_grid.py +0 -0
  211. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/tests/test_tile_grid_selection.py +0 -0
  212. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/tests/test_tile_selector_pick.py +0 -0
  213. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/tests/test_tilemap_save.py +0 -0
  214. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/tests/test_timeline_invalidate_cache.py +0 -0
  215. {tilemap_editor-3.3.10 → tilemap_editor-4.0.0}/tests/test_toolbar_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tilemap-editor
3
- Version: 3.3.10
3
+ Version: 4.0.0
4
4
  Summary: Pygame tilemap editor with SVG icons, professional UI, and sprite animation tools
5
5
  Author: tilemap editor contributors
6
6
  License: Proprietary
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tilemap-editor"
7
- version = "3.3.10"
7
+ version = "4.0.0"
8
8
  description = "Pygame tilemap editor with SVG icons, professional UI, and sprite animation tools"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -37,7 +37,8 @@ from widgets.ui.notification import NotificationManager
37
37
  from widgets.ui.property_editor import PropertyEditor
38
38
  from widgets.ui.particle_config_dialog import ParticleConfigDialog
39
39
  from widgets.ui.tooltip import TooltipManager
40
- from widgets.ui.theme import get_theme_manager, set_theme, THEMES
40
+ from widgets.ui.theme import COLORS, get_theme_manager, set_theme, THEMES
41
+ from widgets.ui.sidebar_container import SidebarContainer, ToolbarAction
41
42
  from utils.log_capture import setup_console_log
42
43
  from utils.standalone import launch_standalone
43
44
  from utils import error_handler, error_context
@@ -148,6 +149,11 @@ class Editor:
148
149
  logger.info(f"Project base_path: {self.base_path}")
149
150
  logger.info(f"Data root: {self.data_root}")
150
151
 
152
+ theme_name = self.config.get("theme", "dark")
153
+ if theme_name in THEMES:
154
+ set_theme(theme_name)
155
+ logger.info(f"Applied theme: {theme_name}")
156
+
151
157
  pygame.init()
152
158
  pygame.display.set_caption("Pure Pygame Editor")
153
159
 
@@ -174,9 +180,8 @@ class Editor:
174
180
  self.tilemap = Tilemap(self)
175
181
 
176
182
  self.selector_w = 300
183
+ self._tileset_dragging = False
177
184
  self.left_panel_w = 380 # Width for the dockable left sidebar
178
- self.tileset_h = 300
179
- self.layer_h = 150
180
185
  self.map_setup_widget: Optional[MapSetup] = None
181
186
  self.map_properties_dialog: Optional[MapPropertiesDialog] = None
182
187
  self.tileset_widget: Optional[TileSelector] = None
@@ -225,16 +230,20 @@ class Editor:
225
230
  self.tilemap.init_size((32, 32), (50, 50))
226
231
 
227
232
  menu_h = 30
228
- self.tileset_widget = TileSelector(
229
- self, self.width - self.selector_w, menu_h, self.selector_w, self.tileset_h
230
- )
231
- self.layer_widget = LayerSelector(
232
- self,
233
- self.width - self.selector_w,
234
- menu_h + self.tileset_h,
235
- self.selector_w,
236
- self.layer_h,
237
- )
233
+ toolbar_h = 35
234
+ self.tileset_widget = TileSelector(self, 0, 0, self.selector_w, 100)
235
+ self.layer_widget = LayerSelector(self, 0, 0, self.selector_w, 100)
236
+ sidebar_x = self.width - self.selector_w
237
+ sidebar_h = self.height - menu_h - toolbar_h
238
+ tileset_actions = [
239
+ ToolbarAction("E", self.tileset_widget._export_selected_as_png_dialog, "E: Export selection as PNG"),
240
+ ToolbarAction("C", self.tileset_widget.open_collision_editor, "Edit Collision Shapes"),
241
+ ToolbarAction("+", self.tileset_widget.request_add_tileset, "Add Tileset"),
242
+ ToolbarAction("-", self.tileset_widget.remove_tileset, "Remove Tileset"),
243
+ ]
244
+ self.sidebar = SidebarContainer(self, Rect(sidebar_x, menu_h + toolbar_h, self.selector_w, sidebar_h))
245
+ self.sidebar.add_tab("Tilesets", self.tileset_widget, tileset_actions)
246
+ self.sidebar.add_tab("Layers", self.layer_widget)
238
247
  self.tile_grid_widget = TileGrid(
239
248
  self,
240
249
  Rect(0, menu_h, self.width - self.selector_w, self.height - menu_h - 25),
@@ -557,15 +566,45 @@ class Editor:
557
566
  return
558
567
 
559
568
  try:
560
- self.post_map_setup()
569
+ self.handle_resize(self.width, self.height)
561
570
  self.tilemap.apply_map_payload(path, payload_or_error)
562
571
  except Exception as e:
563
572
  error_handler.capture(e, context="load_map_apply")
573
+ import traceback
574
+ traceback.print_exc()
564
575
  finally:
565
576
  self.loading_state["active"] = False
566
577
 
567
578
  def post_map_setup(self):
568
579
  self.handle_resize(self.width, self.height)
580
+ if hasattr(self, "tile_grid_widget") and self.tile_grid_widget:
581
+ self.tile_grid_widget.center_on_map()
582
+
583
+ def _update_side_panel_layout(self):
584
+ menu_h = 30
585
+ toolbar_h = 35
586
+ left_offset = self.left_panel_w if self.left_panel_visible else 0
587
+
588
+ min_canvas_w = 200
589
+ available_w = self.width - left_offset
590
+ self._effective_selector_w = min(self.selector_w, max(0, available_w - min_canvas_w))
591
+
592
+ if hasattr(self, "sidebar") and self.sidebar:
593
+ self.sidebar.resize(
594
+ self.width - self._effective_selector_w,
595
+ menu_h + toolbar_h,
596
+ self._effective_selector_w,
597
+ self.height - menu_h - toolbar_h,
598
+ )
599
+
600
+ if hasattr(self, "tile_grid_widget") and self.tile_grid_widget:
601
+ canvas_w = available_w - self._effective_selector_w
602
+ self.tile_grid_widget.rect = Rect(
603
+ left_offset,
604
+ menu_h + toolbar_h,
605
+ canvas_w,
606
+ self.height - (menu_h + toolbar_h) - 25,
607
+ )
569
608
 
570
609
  def handle_resize(self, width: int, height: int):
571
610
  self.width = width
@@ -574,38 +613,13 @@ class Editor:
574
613
  menu_h = 30
575
614
  toolbar_h = 35
576
615
 
577
- # Left sidebar offset
578
- left_offset = self.left_panel_w if self.left_panel_visible else 0
579
-
580
616
  if hasattr(self, "menubar") and self.menubar:
581
617
  self.menubar.resize(width)
582
618
 
583
619
  if hasattr(self, "toolbar") and self.toolbar:
584
620
  self.toolbar.resize(width)
585
621
 
586
- if hasattr(self, "tileset_widget") and self.tileset_widget:
587
- self.tileset_widget.resize(
588
- width - self.selector_w - left_offset,
589
- menu_h + toolbar_h,
590
- self.selector_w,
591
- self.tileset_h,
592
- )
593
-
594
- if hasattr(self, "layer_widget") and self.layer_widget:
595
- self.layer_widget.resize(
596
- width - self.selector_w - left_offset,
597
- menu_h + toolbar_h + self.tileset_h,
598
- self.selector_w,
599
- height - (menu_h + toolbar_h + self.tileset_h),
600
- )
601
-
602
- if hasattr(self, "tile_grid_widget") and self.tile_grid_widget:
603
- self.tile_grid_widget.rect = Rect(
604
- left_offset,
605
- menu_h + toolbar_h,
606
- width - self.selector_w - left_offset,
607
- height - (menu_h + toolbar_h),
608
- )
622
+ self._update_side_panel_layout()
609
623
 
610
624
  # Update animation panel rect
611
625
  if self.left_panel_visible and self.animation_panel:
@@ -673,6 +687,7 @@ class Editor:
673
687
  self.left_panel_visible = True
674
688
  else:
675
689
  self.left_panel_visible = False
690
+ self.handle_resize(self.width, self.height)
676
691
 
677
692
 
678
693
  def _init_animation_panel(self):
@@ -713,9 +728,13 @@ class Editor:
713
728
 
714
729
  def undo(self):
715
730
  self.tilemap.undo()
731
+ if self.tile_grid_widget:
732
+ self.tile_grid_widget.invalidate_bounds_cache()
716
733
 
717
734
  def redo(self):
718
735
  self.tilemap.redo()
736
+ if self.tile_grid_widget:
737
+ self.tile_grid_widget.invalidate_bounds_cache()
719
738
 
720
739
  def toggle_grid(self):
721
740
  if self.tile_grid_widget:
@@ -730,6 +749,17 @@ class Editor:
730
749
  new_theme = theme_names[next_idx]
731
750
  set_theme(new_theme)
732
751
  self.notifications.notify(f"Theme: {new_theme}")
752
+
753
+ settings_file = Path.cwd() / "settings.json"
754
+ if settings_file.exists():
755
+ try:
756
+ with open(settings_file, "r", encoding="utf-8") as f:
757
+ config = json.load(f)
758
+ config["theme"] = new_theme
759
+ with open(settings_file, "w", encoding="utf-8") as f:
760
+ json.dump(config, f, indent=4)
761
+ except Exception:
762
+ pass
733
763
 
734
764
  def launch_external_automap(self):
735
765
  if hasattr(self.autotiler, "_launch_external_viewer"):
@@ -1266,15 +1296,34 @@ class Editor:
1266
1296
  if self.animation_panel.handle_event(event):
1267
1297
  continue
1268
1298
 
1269
- # Priority 7: Side panels (layer widget first so rename captures keys)
1299
+ # Priority 6.5: Sidebar resize drag (vertical)
1300
+ if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
1301
+ mx, my = event.pos
1302
+ edge_x = self.width - self._effective_selector_w
1303
+ if abs(mx - edge_x) < 6:
1304
+ self._tileset_dragging = True
1305
+ continue
1306
+ elif event.type == pygame.MOUSEMOTION:
1307
+ mx, my = event.pos
1308
+ edge_x = self.width - self._effective_selector_w
1309
+ if self._tileset_dragging:
1310
+ new_w = max(150, min(self.width // 2, self.width - mx))
1311
+ self.selector_w = new_w
1312
+ self._update_side_panel_layout()
1313
+ continue
1314
+ near_vertical = abs(mx - edge_x) < 6
1315
+ if near_vertical:
1316
+ pygame.mouse.set_cursor(pygame.SYSTEM_CURSOR_SIZEWE)
1317
+ else:
1318
+ pygame.mouse.set_cursor(pygame.SYSTEM_CURSOR_ARROW)
1319
+ elif event.type == pygame.MOUSEBUTTONUP and event.button == 1:
1320
+ if self._tileset_dragging:
1321
+ self._tileset_dragging = False
1322
+ continue
1323
+
1324
+ # Priority 7: Sidebar container
1270
1325
  consumed = False
1271
- if self.layer_widget and self.layer_widget.handle_event(event):
1272
- consumed = True
1273
- if (
1274
- not consumed
1275
- and self.tileset_widget
1276
- and self.tileset_widget.handle_event(event)
1277
- ):
1326
+ if self.sidebar and self.sidebar.handle_event(event):
1278
1327
  consumed = True
1279
1328
 
1280
1329
  # Priority 8: Main tile grid (lowest priority)
@@ -1306,10 +1355,13 @@ class Editor:
1306
1355
 
1307
1356
  if self.tile_grid_widget:
1308
1357
  self.tile_grid_widget.draw(self.screen)
1309
- if self.tileset_widget:
1310
- self.tileset_widget.draw(self.screen)
1311
- if self.layer_widget:
1312
- self.layer_widget.draw(self.screen)
1358
+ if self.sidebar:
1359
+ self.sidebar.draw(self.screen)
1360
+ edge_x = self.width - self._effective_selector_w
1361
+ handle_rect = Rect(edge_x - 1, 0, 3, self.height)
1362
+ pygame.draw.rect(self.screen, COLORS.border, handle_rect)
1363
+ if self._tileset_dragging:
1364
+ pygame.draw.rect(self.screen, COLORS.accent, handle_rect)
1313
1365
 
1314
1366
  # Draw autotiler and regex designer with modal overlay
1315
1367
  if self.autotiler:
@@ -12,11 +12,11 @@ from typing import Callable, List, Optional, Tuple
12
12
  import pygame
13
13
  from pygame import Rect
14
14
 
15
- from .models import AnimationFrame
16
- from utils.font_manager import font_manager, FontWeight, FontStyle
15
+ from utils.font_manager import FontStyle, FontWeight, font_manager
17
16
  from utils.icon_manager import icon_manager
18
17
 
19
- # ---------------------------------------------------------------------------
18
+ from .models import AnimationFrame
19
+
20
20
  _COLORS = {
21
21
  "bg": (25, 27, 30),
22
22
  "panel": (35, 38, 44),
@@ -39,7 +39,6 @@ _COLORS = {
39
39
 
40
40
  HEADER_H = 36
41
41
  CONTROLS_H = 32
42
- # Preview timing vs a 60 Hz editor tick: elapsed += dt_ms * (playback_fps / 60).
43
42
 
44
43
 
45
44
  class AnimationPreview:
@@ -76,7 +75,6 @@ class AnimationPreview:
76
75
  self._editing_fps = False
77
76
  self._fps_input_rect = Rect(0, 0, 0, 0)
78
77
 
79
- # Buttons (recalculated in draw)
80
78
  self._btn_play = Rect(0, 0, 0, 0)
81
79
  self._btn_stop = Rect(0, 0, 0, 0)
82
80
  self._btn_loop = Rect(0, 0, 0, 0)
@@ -84,24 +82,21 @@ class AnimationPreview:
84
82
  self._btn_prev = Rect(0, 0, 0, 0)
85
83
  self._btn_next = Rect(0, 0, 0, 0)
86
84
 
87
- # Scrubber fraction output (read by timeline)
88
85
  self.scrubber_frac: float = 0.0
89
86
 
90
87
  self._font: Optional[pygame.font.Font] = None
91
88
  self._font_sm: Optional[pygame.font.Font] = None
92
89
  self._checker: Optional[pygame.Surface] = None
93
90
 
94
- # ------------------------------------------------------------------
95
- # Public
96
- # ------------------------------------------------------------------
97
-
98
91
  def set_frames(self, frames: List[AnimationFrame]) -> None:
99
92
  self.frames = frames
100
93
  self.current_frame = 0
101
94
  self._elapsed = 0.0
102
95
 
103
96
  def set_surface(
104
- self, surface: pygame.Surface, tile_size: Optional[Tuple[int, int]] = None
97
+ self,
98
+ surface: pygame.Surface,
99
+ tile_size: Optional[Tuple[int, int]] = None
105
100
  ) -> None:
106
101
  self.surface = surface
107
102
  if tile_size:
@@ -154,10 +149,6 @@ class AnimationPreview:
154
149
  self._elapsed = 0.0
155
150
  self.scrubber_frac = 0.0
156
151
 
157
- # ------------------------------------------------------------------
158
- # Update (call every frame with dt in milliseconds)
159
- # ------------------------------------------------------------------
160
-
161
152
  def update(self, dt_ms: float) -> None:
162
153
  if not self.playing or not self.frames:
163
154
  return
@@ -178,7 +169,6 @@ class AnimationPreview:
178
169
  break
179
170
  cur = self.frames[self.current_frame]
180
171
 
181
- # Compute scrubber fraction
182
172
  total = sum(f.duration_ms for f in self.frames)
183
173
  if total > 0:
184
174
  elapsed_to_cur = (
@@ -189,21 +179,13 @@ class AnimationPreview:
189
179
  else:
190
180
  self.scrubber_frac = 0.0
191
181
 
192
- # ------------------------------------------------------------------
193
- # Events
194
- # ------------------------------------------------------------------
195
-
196
182
  def handle_event(self, event: pygame.event.Event) -> bool:
197
183
  if event.type == pygame.KEYDOWN and self._editing_fps:
198
184
  return self._handle_fps_keydown(event)
199
185
 
200
186
  mouse = pygame.mouse.get_pos()
201
187
  if not self.rect.collidepoint(mouse):
202
- if (
203
- event.type == pygame.MOUSEBUTTONDOWN
204
- and event.button == 1
205
- and self._editing_fps
206
- ):
188
+ if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1 and self._editing_fps:
207
189
  self.commit_fps_input()
208
190
  return False
209
191
 
@@ -237,7 +219,6 @@ class AnimationPreview:
237
219
  self._step(1)
238
220
  return True
239
221
 
240
- # Scroll to adjust zoom
241
222
  if event.type == pygame.MOUSEWHEEL and self.rect.collidepoint(mouse):
242
223
  self._zoom *= 1.15 if event.y > 0 else 0.87
243
224
  self._zoom = max(1.0, min(self._zoom, 12.0))
@@ -245,10 +226,6 @@ class AnimationPreview:
245
226
 
246
227
  return False
247
228
 
248
- # ------------------------------------------------------------------
249
- # Drawing
250
- # ------------------------------------------------------------------
251
-
252
229
  def draw(self, screen: pygame.Surface) -> None:
253
230
  self._ensure_fonts()
254
231
  clip = screen.get_clip()
@@ -257,15 +234,12 @@ class AnimationPreview:
257
234
  pygame.draw.rect(screen, _COLORS["panel"], self.rect)
258
235
  pygame.draw.rect(screen, _COLORS["border"], self.rect, 1)
259
236
 
260
- # Header
261
237
  hdr = Rect(self.rect.x, self.rect.y, self.rect.w, HEADER_H)
262
238
  pygame.draw.rect(screen, _COLORS["header"], hdr)
263
239
  title = "Preview"
264
240
  if self.frames:
265
241
  title += f" {self.current_frame + 1}/{len(self.frames)}"
266
- screen.blit(
267
- self._font.render(title, True, _COLORS["text"]), (hdr.x + 6, hdr.y + 2)
268
- )
242
+ screen.blit(self._font.render(title, True, _COLORS["text"]), (hdr.x + 6, hdr.y + 2))
269
243
  if self.frames and 0 <= self.current_frame < len(self.frames):
270
244
  fr = self.frames[self.current_frame]
271
245
  auth = max(0.001, float(self.authoring_fps))
@@ -276,19 +250,15 @@ class AnimationPreview:
276
250
  (hdr.x + 6, hdr.y + 18),
277
251
  )
278
252
 
279
- # Preview area
280
253
  preview_y = self.rect.y + HEADER_H
281
254
  preview_h = self.rect.h - HEADER_H - CONTROLS_H
282
255
  preview_rect = Rect(self.rect.x, preview_y, self.rect.w, preview_h)
283
256
 
284
- # Checkerboard background
285
257
  self._draw_checker(screen, preview_rect)
286
258
 
287
- # Draw current frame
288
259
  if self.frames and 0 <= self.current_frame < len(self.frames):
289
260
  frame = self.frames[self.current_frame]
290
261
 
291
- # Onion skin (previous frame)
292
262
  if self.show_onion and self.current_frame > 0:
293
263
  prev_frame = self.frames[self.current_frame - 1]
294
264
  prev_surf = self._extract_tile(prev_frame.variant_id)
@@ -305,7 +275,6 @@ class AnimationPreview:
305
275
  dy = preview_rect.centery - zoomed.get_height() // 2
306
276
  screen.blit(zoomed, (dx, dy))
307
277
 
308
- # Current frame
309
278
  tile_surf = self._extract_tile(frame.variant_id)
310
279
  if tile_surf:
311
280
  zw = int(self.tile_size[0] * self._zoom)
@@ -319,7 +288,6 @@ class AnimationPreview:
319
288
  no_anim = self._font.render("No frames", True, _COLORS["text_dim"])
320
289
  screen.blit(no_anim, no_anim.get_rect(center=preview_rect.center))
321
290
 
322
- # Controls bar
323
291
  ctrl_y = self.rect.bottom - CONTROLS_H
324
292
  ctrl_rect = Rect(self.rect.x, ctrl_y, self.rect.w, CONTROLS_H)
325
293
  pygame.draw.rect(screen, _COLORS["header"], ctrl_rect)
@@ -328,10 +296,6 @@ class AnimationPreview:
328
296
 
329
297
  screen.set_clip(clip)
330
298
 
331
- # ------------------------------------------------------------------
332
- # Controls drawing
333
- # ------------------------------------------------------------------
334
-
335
299
  def _draw_controls(self, screen: pygame.Surface, ctrl_rect: Rect) -> None:
336
300
  mouse = pygame.mouse.get_pos()
337
301
  bw, bh = 28, 24
@@ -339,53 +303,43 @@ class AnimationPreview:
339
303
  x = ctrl_rect.x + pad
340
304
  y = ctrl_rect.y + (ctrl_rect.h - bh) // 2
341
305
 
342
- # |<< (prev)
343
306
  self._btn_prev = Rect(x, y, bw, bh)
344
307
  self._draw_btn(screen, self._btn_prev, "", mouse)
345
308
  prev_icon = icon_manager.get_icon("skip-back", 12, _COLORS["text"])
346
309
  screen.blit(prev_icon, prev_icon.get_rect(center=self._btn_prev.center))
347
310
  x += bw + pad
348
311
 
349
- # Play / Pause
350
312
  self._btn_play = Rect(x, y, bw, bh)
351
313
  self._draw_btn(screen, self._btn_play, "", mouse, active=self.playing)
352
- play_icon = icon_manager.get_icon(
353
- "pause" if self.playing else "play", 12, _COLORS["text"]
354
- )
314
+ play_icon = icon_manager.get_icon("pause" if self.playing else "play", 12, _COLORS["text"])
355
315
  screen.blit(play_icon, play_icon.get_rect(center=self._btn_play.center))
356
316
  x += bw + pad
357
317
 
358
- # Stop
359
318
  self._btn_stop = Rect(x, y, bw, bh)
360
319
  self._draw_btn(screen, self._btn_stop, "", mouse)
361
320
  stop_icon = icon_manager.get_icon("stop", 12, _COLORS["text"])
362
321
  screen.blit(stop_icon, stop_icon.get_rect(center=self._btn_stop.center))
363
322
  x += bw + pad
364
323
 
365
- # >>| (next)
366
324
  self._btn_next = Rect(x, y, bw, bh)
367
325
  self._draw_btn(screen, self._btn_next, "", mouse)
368
326
  next_icon = icon_manager.get_icon("skip-forward", 12, _COLORS["text"])
369
327
  screen.blit(next_icon, next_icon.get_rect(center=self._btn_next.center))
370
328
  x += bw + pad + 4
371
329
 
372
- # Loop toggle
373
330
  self._btn_loop = Rect(x, y, bw + 4, bh)
374
331
  self._draw_btn(screen, self._btn_loop, "", mouse, active=self.loop)
375
332
  loop_icon = icon_manager.get_icon("loop", 12, _COLORS["text"])
376
333
  screen.blit(loop_icon, loop_icon.get_rect(center=self._btn_loop.center))
377
334
  x += bw + pad + 8
378
335
 
379
- # Playback FPS (editable; timing vs 60 Hz tick — see update())
380
336
  fps_tag = self._font_sm.render("FPS", True, _COLORS["text_dim"])
381
337
  screen.blit(fps_tag, (x, y + (bh - fps_tag.get_height()) // 2))
382
338
  x += fps_tag.get_width() + 3
383
339
  self._fps_input_rect = Rect(x, y, 44, bh)
384
340
  fps_bg = _COLORS["btn_active"] if self._editing_fps else _COLORS["input_bg"]
385
341
  pygame.draw.rect(screen, fps_bg, self._fps_input_rect, border_radius=3)
386
- pygame.draw.rect(
387
- screen, _COLORS["border"], self._fps_input_rect, 1, border_radius=3
388
- )
342
+ pygame.draw.rect(screen, _COLORS["border"], self._fps_input_rect, 1, border_radius=3)
389
343
  shown = self._fps_input_text
390
344
  if self._editing_fps and (pygame.time.get_ticks() // 400) % 2:
391
345
  shown += "|"
@@ -394,16 +348,13 @@ class AnimationPreview:
394
348
  screen.blit(fps_surf, (self._fps_input_rect.x + 4, self._fps_input_rect.y + 5))
395
349
  x += 44 + pad
396
350
 
397
- # Onion skin
398
351
  if x + bw + 12 < ctrl_rect.right:
399
352
  self._btn_onion = Rect(x, y, bw + 4, bh)
400
353
  self._draw_btn(screen, self._btn_onion, "", mouse, active=self.show_onion)
401
354
  onion_icon = icon_manager.get_icon("onion", 14, _COLORS["text"])
402
355
  screen.blit(onion_icon, onion_icon.get_rect(center=self._btn_onion.center))
403
356
 
404
- def _draw_btn(
405
- self, screen: pygame.Surface, rect: Rect, label: str, mouse, active=False
406
- ) -> None:
357
+ def _draw_btn(self, screen: pygame.Surface, rect: Rect, label: str, mouse, active=False) -> None:
407
358
  hover = rect.collidepoint(mouse)
408
359
  if active:
409
360
  bg = _COLORS["btn_active"]
@@ -416,10 +367,6 @@ class AnimationPreview:
416
367
  lbl = self._font_sm.render(label, True, _COLORS["text"])
417
368
  screen.blit(lbl, lbl.get_rect(center=rect.center))
418
369
 
419
- # ------------------------------------------------------------------
420
- # Helpers
421
- # ------------------------------------------------------------------
422
-
423
370
  def _extract_tile(self, variant_id: int) -> Optional[pygame.Surface]:
424
371
  tw, th = self.tile_size
425
372
  ox = self.grid_offset_x
@@ -625,7 +625,7 @@ class TilesetCollisionEditor:
625
625
  """Save collision data to file, auto-propagating within auto-tile groups."""
626
626
  try:
627
627
  self._save_tile_collision_for_selection()
628
- self._propagate_collision_to_groups()
628
+ # self._propagate_collision_to_groups() # disabled — auto-copies collision to all autotile variants, breaks per-variant shapes
629
629
  self.library.save(path)
630
630
  except Exception as e:
631
631
  error_handler.capture(e, context="save_collision_file")
@@ -477,8 +477,7 @@ class Tilemap:
477
477
  try:
478
478
  self.tile_size = deserialize_point(payload["meta"]["tile_size"])
479
479
  self.map_size = deserialize_point(payload["meta"].get("map_size", "50;50"))
480
- # If the file tells us the map is large, we assume that was intended
481
- # But the 'initial' setup might be in meta or we use map_size as base
480
+
482
481
  self.initial_map_size = payload["meta"].get("initial_map_size")
483
482
  if self.initial_map_size:
484
483
  self.initial_map_size = deserialize_point(self.initial_map_size)
@@ -488,16 +487,6 @@ class Tilemap:
488
487
  except (KeyError, ValueError) as e:
489
488
  raise ValueError(f"Error loading map metadata: {e}") from e
490
489
 
491
- # Restore view state
492
- if self.editor.tile_grid_widget:
493
- if "zoom_level" in payload["meta"]:
494
- self.editor.tile_grid_widget.zoom_level = payload["meta"]["zoom_level"]
495
- if "scroll" in payload["meta"]:
496
- scroll = deserialize_point(payload["meta"]["scroll"])
497
- self.editor.tile_grid_widget.scroll_x = scroll[0]
498
- self.editor.tile_grid_widget.scroll_y = scroll[1]
499
- self.editor.tile_grid_widget.clamp_view()
500
-
501
490
  resources = payload.get("resources", {})
502
491
  tilesets = []
503
492
  if isinstance(resources, list):
@@ -669,6 +658,18 @@ class Tilemap:
669
658
 
670
659
  self.initialized = True
671
660
 
661
+ # Restore view state and invalidate bounds cache after map load
662
+ if self.editor.tile_grid_widget:
663
+ if "zoom_level" in payload["meta"]:
664
+ self.editor.tile_grid_widget.zoom_level = payload["meta"]["zoom_level"]
665
+ if "scroll" in payload["meta"]:
666
+ scroll = deserialize_point(payload["meta"]["scroll"])
667
+ self.editor.tile_grid_widget.scroll_x = scroll[0]
668
+ self.editor.tile_grid_widget.scroll_y = scroll[1]
669
+ if hasattr(self.editor.tile_grid_widget, "clamp_scroll"):
670
+ self.editor.tile_grid_widget.clamp_scroll()
671
+ self.editor.tile_grid_widget.invalidate_bounds_cache()
672
+
672
673
  def _object_tileset_indices_from_payload(self, payload: dict) -> set[int]:
673
674
  """Infer legacy object tileset resources from object-layer references."""
674
675
  indices: set[int] = set()
@@ -25,6 +25,8 @@ def init_settings(generate_main: bool = False) -> None:
25
25
  "character": "character_collision"
26
26
  },
27
27
  "nodes_path": "nodes",
28
+ "theme": "dark",
29
+ "themes_list": ["dark", "molokai", "light", "semi_light"],
28
30
  "error_handler": {
29
31
  "log_path": "errors.log",
30
32
  "max_recent_errors": 50,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tilemap-editor
3
- Version: 3.3.10
3
+ Version: 4.0.0
4
4
  Summary: Pygame tilemap editor with SVG icons, professional UI, and sprite animation tools
5
5
  Author: tilemap editor contributors
6
6
  License: Proprietary
@@ -172,12 +172,16 @@ src/widgets/regex_automap_designer.py
172
172
  src/widgets/spritesheet_grid.py
173
173
  src/widgets/tile_grid.py
174
174
  src/widgets/tile_selector.py
175
+ src/widgets/widget_base.py
176
+ src/widgets/ui/button.py
175
177
  src/widgets/ui/checkbox.py
176
178
  src/widgets/ui/collision_layer_mask.py
177
179
  src/widgets/ui/collision_layer_sidebar.py
178
180
  src/widgets/ui/confirm_dialog.py
181
+ src/widgets/ui/dialog_base.py
179
182
  src/widgets/ui/draw_utils.py
180
183
  src/widgets/ui/fileinput.py
184
+ src/widgets/ui/label.py
181
185
  src/widgets/ui/layer_type_dialog.py
182
186
  src/widgets/ui/menubar.py
183
187
  src/widgets/ui/mode_indicator.py
@@ -187,6 +191,8 @@ src/widgets/ui/notification.py
187
191
  src/widgets/ui/particle_config_dialog.py
188
192
  src/widgets/ui/property_editor.py
189
193
  src/widgets/ui/region_selector.py
194
+ src/widgets/ui/scrollbar.py
195
+ src/widgets/ui/sidebar_container.py
190
196
  src/widgets/ui/status_bar.py
191
197
  src/widgets/ui/theme.py
192
198
  src/widgets/ui/tileset_type_dialog.py
@@ -198,6 +204,7 @@ tests/test_collision_layer_sidebar.py
198
204
  tests/test_editor_pan_mode.py
199
205
  tests/test_project_paths.py
200
206
  tests/test_render_scale.py
207
+ tests/test_serialization_regression.py
201
208
  tests/test_sprite_animation_editor_grid.py
202
209
  tests/test_tile_grid_selection.py
203
210
  tests/test_tile_selector_pick.py