tilemap-editor 3.2.10__tar.gz → 3.3.1__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 (198) hide show
  1. {tilemap_editor-3.2.10/src/tilemap_editor.egg-info → tilemap_editor-3.3.1}/PKG-INFO +1 -1
  2. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/pyproject.toml +3 -1
  3. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/editor.py +67 -12
  4. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/sprite_editor/editor.py +161 -8
  5. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/sprite_editor/standalone.py +23 -17
  6. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1/src/tilemap_editor.egg-info}/PKG-INFO +1 -1
  7. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor.egg-info/top_level.txt +2 -0
  8. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/widgets/filemanager.py +13 -4
  9. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/widgets/tile_grid.py +5 -4
  10. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/widgets/ui/menubar.py +5 -0
  11. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/widgets/ui/tileset_type_dialog.py +2 -2
  12. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/LICENSE +0 -0
  13. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/MANIFEST.in +0 -0
  14. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/README.md +0 -0
  15. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
  16. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
  17. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
  18. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
  19. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
  20. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
  21. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
  22. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
  23. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/icons/arrow-down.svg +0 -0
  24. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/icons/check.svg +0 -0
  25. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/icons/checked.svg +0 -0
  26. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/icons/close.svg +0 -0
  27. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/icons/duplicate.svg +0 -0
  28. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/icons/error.svg +0 -0
  29. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/icons/file.svg +0 -0
  30. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/icons/filedead.svg +0 -0
  31. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/icons/fit.svg +0 -0
  32. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/icons/folder.svg +0 -0
  33. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/icons/grid.svg +0 -0
  34. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/icons/image.svg +0 -0
  35. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/icons/info.svg +0 -0
  36. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/icons/load.svg +0 -0
  37. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/icons/loop.svg +0 -0
  38. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/icons/pan.svg +0 -0
  39. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/icons/pause.svg +0 -0
  40. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/icons/pencil.svg +0 -0
  41. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/icons/play.svg +0 -0
  42. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/icons/plus.svg +0 -0
  43. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/icons/radio.svg +0 -0
  44. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/icons/reset.svg +0 -0
  45. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/icons/save.svg +0 -0
  46. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/icons/stop.svg +0 -0
  47. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/icons/tilemap.svg +0 -0
  48. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/icons/tileset.svg +0 -0
  49. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/icons/unchecked.svg +0 -0
  50. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/icons/warning.svg +0 -0
  51. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/icons/zoomin.svg +0 -0
  52. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/assets/icons/zoomout.svg +0 -0
  53. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/setup.cfg +0 -0
  54. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/configs/themes.py +0 -0
  55. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/constants.py +0 -0
  56. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/event_map.py +0 -0
  57. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/layers.py +0 -0
  58. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/main.py +0 -0
  59. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/node_manager.py +0 -0
  60. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/nodes.py +0 -0
  61. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/__init__.py +0 -0
  62. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/character_collision/__init__.py +0 -0
  63. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/character_collision/editor.py +0 -0
  64. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/character_collision/models.py +0 -0
  65. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/character_collision/protocols.py +0 -0
  66. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/character_collision/shape_editor.py +0 -0
  67. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/character_collision/standalone.py +0 -0
  68. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/object_tileset_collision/__init__.py +0 -0
  69. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/object_tileset_collision/editor.py +0 -0
  70. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/object_tileset_collision/models.py +0 -0
  71. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/object_tileset_collision/protocols.py +0 -0
  72. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/object_tileset_collision/standalone.py +0 -0
  73. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/sprite_animation/__init__.py +0 -0
  74. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/sprite_animation/__main__.py +0 -0
  75. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/sprite_animation/clipboard_util.py +0 -0
  76. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/sprite_animation/editor.py +0 -0
  77. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/sprite_animation/frame_picker.py +0 -0
  78. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/sprite_animation/models.py +0 -0
  79. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/sprite_animation/preview.py +0 -0
  80. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/sprite_animation/protocols.py +0 -0
  81. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/sprite_animation/runtime_load.py +0 -0
  82. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/sprite_animation/standalone.py +0 -0
  83. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/sprite_animation/timeline.py +0 -0
  84. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/sprite_animation/validation.py +0 -0
  85. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/sprite_editor/__init__.py +0 -0
  86. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/tileset_collision/__init__.py +0 -0
  87. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/tileset_collision/collision_painter.py +0 -0
  88. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/tileset_collision/editor.py +0 -0
  89. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/tileset_collision/models.py +0 -0
  90. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/tileset_collision/protocols.py +0 -0
  91. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/plugins/tileset_collision/standalone.py +0 -0
  92. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/standalone_automap.py +0 -0
  93. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/standalone_error_console.py +0 -0
  94. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/standalone_filemanager.py +0 -0
  95. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/standalone_image_viewer.py +0 -0
  96. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap.py +0 -0
  97. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/__init__.py +0 -0
  98. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/__main__.py +0 -0
  99. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/__init__.py +0 -0
  100. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
  101. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
  102. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
  103. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
  104. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
  105. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
  106. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
  107. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
  108. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/icons/ToolMove.svg +0 -0
  109. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/icons/arrow-down.svg +0 -0
  110. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/icons/check.svg +0 -0
  111. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/icons/checked.svg +0 -0
  112. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/icons/close.svg +0 -0
  113. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/icons/duplicate.svg +0 -0
  114. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/icons/error.svg +0 -0
  115. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/icons/file.svg +0 -0
  116. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/icons/filedead.svg +0 -0
  117. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/icons/fit.svg +0 -0
  118. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/icons/folder.svg +0 -0
  119. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/icons/grid.svg +0 -0
  120. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/icons/image.svg +0 -0
  121. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/icons/info.svg +0 -0
  122. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/icons/load.svg +0 -0
  123. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/icons/loop.svg +0 -0
  124. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/icons/pan.svg +0 -0
  125. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/icons/pause.svg +0 -0
  126. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/icons/pencil.svg +0 -0
  127. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/icons/play.svg +0 -0
  128. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/icons/plus.svg +0 -0
  129. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/icons/radio.svg +0 -0
  130. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/icons/reset.svg +0 -0
  131. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/icons/save.svg +0 -0
  132. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/icons/stop.svg +0 -0
  133. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/icons/tilemap.svg +0 -0
  134. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/icons/tileset.svg +0 -0
  135. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/icons/unchecked.svg +0 -0
  136. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/icons/warning.svg +0 -0
  137. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/icons/zoomin.svg +0 -0
  138. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/assets/icons/zoomout.svg +0 -0
  139. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/cli.py +0 -0
  140. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/main.py +0 -0
  141. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor/settings.py +0 -0
  142. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor.egg-info/SOURCES.txt +0 -0
  143. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor.egg-info/dependency_links.txt +0 -0
  144. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor.egg-info/entry_points.txt +0 -0
  145. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/tilemap_editor.egg-info/requires.txt +0 -0
  146. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/ttypes/__init__.py +0 -0
  147. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/ttypes/tilemap.py +0 -0
  148. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/utils/__init__.py +0 -0
  149. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/utils/editor_preference.py +0 -0
  150. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/utils/error_handler.py +0 -0
  151. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/utils/font_manager.py +0 -0
  152. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/utils/history.py +0 -0
  153. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/utils/icon_manager.py +0 -0
  154. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/utils/icons_cache.py +0 -0
  155. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/utils/log_capture.py +0 -0
  156. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/utils/project_paths.py +0 -0
  157. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/utils/serialization.py +0 -0
  158. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/utils/standalone.py +0 -0
  159. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/utils/validation.py +0 -0
  160. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/widgets/__init__.py +0 -0
  161. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/widgets/automap_models.py +0 -0
  162. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/widgets/autotile_template.py +0 -0
  163. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/widgets/autotiler.py +0 -0
  164. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/widgets/input.py +0 -0
  165. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/widgets/layer_selector.py +0 -0
  166. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/widgets/map_properties.py +0 -0
  167. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/widgets/mapsetup.py +0 -0
  168. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/widgets/regex_automap_designer.py +0 -0
  169. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/widgets/spritesheet_grid.py +0 -0
  170. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/widgets/tile_selector.py +0 -0
  171. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/widgets/ui/collision_layer_mask.py +0 -0
  172. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/widgets/ui/collision_layer_sidebar.py +0 -0
  173. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/widgets/ui/confirm_dialog.py +0 -0
  174. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/widgets/ui/draw_utils.py +0 -0
  175. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/widgets/ui/fileinput.py +0 -0
  176. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/widgets/ui/layer_type_dialog.py +0 -0
  177. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/widgets/ui/mode_indicator.py +0 -0
  178. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/widgets/ui/node_editor.py +0 -0
  179. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/widgets/ui/node_selector.py +0 -0
  180. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/widgets/ui/notification.py +0 -0
  181. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/widgets/ui/property_editor.py +0 -0
  182. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/widgets/ui/region_selector.py +0 -0
  183. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/widgets/ui/status_bar.py +0 -0
  184. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/widgets/ui/theme.py +0 -0
  185. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/widgets/ui/toolbar.py +0 -0
  186. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/src/widgets/ui/tooltip.py +0 -0
  187. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/tests/test_animation_library_grid_offset.py +0 -0
  188. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/tests/test_collision_layer_mask.py +0 -0
  189. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/tests/test_collision_layer_sidebar.py +0 -0
  190. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/tests/test_editor_pan_mode.py +0 -0
  191. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/tests/test_project_paths.py +0 -0
  192. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/tests/test_render_scale.py +0 -0
  193. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/tests/test_sprite_animation_editor_grid.py +0 -0
  194. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/tests/test_tile_grid_selection.py +0 -0
  195. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/tests/test_tile_selector_pick.py +0 -0
  196. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/tests/test_tilemap_save.py +0 -0
  197. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/tests/test_timeline_invalidate_cache.py +0 -0
  198. {tilemap_editor-3.2.10 → tilemap_editor-3.3.1}/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.2.10
3
+ Version: 3.3.1
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.2.10"
7
+ version = "3.3.1"
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"
@@ -32,6 +32,8 @@ py-modules = [
32
32
  "editor",
33
33
  "event_map",
34
34
  "layers",
35
+ "node_manager",
36
+ "nodes",
35
37
  "tilemap",
36
38
  "standalone_filemanager",
37
39
  "standalone_image_viewer",
@@ -242,7 +242,7 @@ class Editor:
242
242
  center_x = (self.width - 400) // 2
243
243
  center_y = (self.height - 400) // 2
244
244
  self.map_setup_widget = MapSetup(self, Rect(center_x, center_y, 400, 400))
245
- self.map_setup_widget.visible = False
245
+ self.map_setup_widget.visible = True
246
246
  self.map_properties_dialog = MapPropertiesDialog(self, Rect(center_x, center_y, 400, 260))
247
247
  self.node_manager = NodeManager(self)
248
248
  self.node_selector = NodeSelector(self, 0, 65, 260, 240)
@@ -442,6 +442,67 @@ class Editor:
442
442
  except Exception as e:
443
443
  error_handler.capture(e, context="save_map_selected")
444
444
 
445
+ def export_selection_as_png(self):
446
+ rect = self.tile_grid_widget.selection_rect
447
+ if rect is None:
448
+ self.notifications.notify("No region selected", duration=2.0)
449
+ return
450
+ self.open_file_manager(
451
+ on_save=self._on_export_selection_png,
452
+ allowed_exts=[".png"],
453
+ mode="save",
454
+ default_name="selection.png",
455
+ )
456
+
457
+ def _on_export_selection_png(self, path: Path):
458
+ rect = self.tile_grid_widget.selection_rect
459
+ if rect is None:
460
+ return
461
+ x1, y1, x2, y2 = rect
462
+ tw, th = self.tilemap.tile_size
463
+ pw = (x2 - x1 + 1) * tw
464
+ ph = (y2 - y1 + 1) * th
465
+ if pw <= 0 or ph <= 0:
466
+ return
467
+
468
+ export_surf = pygame.Surface((pw, ph), pygame.SRCALPHA)
469
+ layers = self.tilemap.layer_manager.get_rendered_layers()
470
+ tileset_map = self.tileset_widget.tileset_map
471
+
472
+ for layer in layers:
473
+ if layer.layer_type != "tile":
474
+ continue
475
+ if layer.opacity < 1.0:
476
+ layer_surf = pygame.Surface((pw, ph), pygame.SRCALPHA)
477
+ else:
478
+ layer_surf = export_surf
479
+
480
+ for (gx, gy), tile in layer.tiles.items():
481
+ if not (x1 <= gx <= x2 and y1 <= gy <= y2):
482
+ continue
483
+ dx = (gx - x1) * tw
484
+ dy = (gy - y1) * th
485
+ ttype = int(tile["ttype"])
486
+ if ttype not in tileset_map:
487
+ continue
488
+ ts = tileset_map[ttype]
489
+ variant_id = tile["variant"]
490
+ sheet_w = ts.surface.get_width()
491
+ sheet_cols = sheet_w // tw
492
+ src_x = (variant_id % sheet_cols) * tw
493
+ src_y = (variant_id // sheet_cols) * th
494
+ src_rect = Rect(src_x, src_y, tw, th)
495
+ if not ts.surface.get_rect().contains(src_rect):
496
+ continue
497
+ layer_surf.blit(ts.surface, (dx, dy), src_rect)
498
+
499
+ if layer.opacity < 1.0:
500
+ layer_surf.set_alpha(int(layer.opacity * 255))
501
+ export_surf.blit(layer_surf, (0, 0))
502
+
503
+ pygame.image.save(export_surf, str(path))
504
+ self.notifications.notify(f"Exported selection to {path.name}", duration=2.0)
505
+
445
506
  def perform_load(self):
446
507
  self.open_file_manager(
447
508
  on_select=self.on_map_file_selected,
@@ -771,23 +832,14 @@ class Editor:
771
832
  error_handler.capture(e, context="launch_animation_editor")
772
833
 
773
834
  def launch_sprite_editor(self):
774
- """Launch the sprite editor in a new window."""
775
- self.open_file_manager(
776
- on_select=self._launch_sprite_editor_with_image,
777
- initial_dir=self.data_root,
778
- allowed_exts=[".png", ".jpg", ".jpeg"],
779
- mode="open",
780
- )
781
-
782
- def _launch_sprite_editor_with_image(self, path: Path):
783
- """Launch sprite editor subprocess with selected image."""
835
+ """Launch the sprite editor (blank) in a new window. Use [Open] inside to load sheets."""
784
836
  try:
785
837
  tile_size = "32x32"
786
838
  if hasattr(self.tilemap, "tile_size") and self.tilemap.tile_size:
787
839
  tw, th = self.tilemap.tile_size
788
840
  tile_size = f"{tw}x{th}"
789
841
 
790
- args = [str(path), "--tile-size", tile_size, "--data-root", str(self.data_root)]
842
+ args = ["--tile-size", tile_size, "--data-root", str(self.data_root)]
791
843
  process = launch_standalone(
792
844
  "plugins.sprite_editor.standalone",
793
845
  args,
@@ -1161,6 +1213,9 @@ class Editor:
1161
1213
  elif event.mod & pygame.KMOD_CTRL and event.key == pygame.K_g:
1162
1214
  self.toggle_grid()
1163
1215
  continue
1216
+ elif event.key == pygame.K_e and ctrl_held and shift_held:
1217
+ self.export_selection_as_png()
1218
+ continue
1164
1219
  elif event.key == pygame.K_t and (ctrl_held or meta_held):
1165
1220
  self.cycle_theme()
1166
1221
  continue
@@ -2,16 +2,16 @@
2
2
  SpriteEditor — standalone widget wrapping SpritesheetGrid with editing toolbar.
3
3
 
4
4
  Toolbar:
5
- [Flip X] [Flip Y] [Copy] [Paste] [Undo] [Redo] [Scale...] [Save PNG]
5
+ [Flip X] [Flip Y] [Copy] [Paste] [Undo] [Redo] [Scale...] [Grid WxH] [Open] [Save PNG]
6
6
  """
7
7
 
8
8
  from __future__ import annotations
9
9
 
10
10
  from pathlib import Path
11
- from typing import Dict, Optional
11
+ from typing import Dict, List, Optional, Union
12
12
 
13
13
  import pygame
14
- from pygame import Rect, Surface
14
+ from pygame import Rect, Surface, SRCALPHA
15
15
 
16
16
  from widgets.spritesheet_grid import SpritesheetGrid
17
17
  from widgets.ui.theme import COLORS
@@ -34,6 +34,8 @@ _BUTTON_ICONS: dict[str, str | None] = {
34
34
  "Undo": None,
35
35
  "Redo": None,
36
36
  "Scale": None,
37
+ "Grid": None,
38
+ "Open": None,
37
39
  "Save": "save",
38
40
  }
39
41
 
@@ -42,8 +44,8 @@ class SpriteEditor:
42
44
  def __init__(
43
45
  self,
44
46
  rect: Rect,
45
- surface: Surface,
46
- tile_size: tuple[int, int],
47
+ surface: Optional[Surface] = None,
48
+ tile_size: tuple[int, int] = (32, 32),
47
49
  image_path: Optional[Path] = None,
48
50
  data_root: Optional[Path] = None,
49
51
  ):
@@ -51,9 +53,17 @@ class SpriteEditor:
51
53
  self.image_path = image_path
52
54
  self._data_root = data_root or (image_path.parent if image_path else Path.cwd())
53
55
 
56
+ # Start blank if no surface provided; track real sheets separately
57
+ if surface is None:
58
+ placeholder = Surface((1, 1), SRCALPHA)
59
+ self._sheet_surfaces: List[Surface] = []
60
+ else:
61
+ placeholder = surface
62
+ self._sheet_surfaces = [surface]
63
+
54
64
  # Grid
55
65
  grid_rect = Rect(rect.x, rect.y + TOOLBAR_H, rect.w, rect.h - TOOLBAR_H - STATUS_H)
56
- self.grid = SpritesheetGrid(grid_rect, surface, tile_size)
66
+ self.grid = SpritesheetGrid(grid_rect, placeholder, tile_size)
57
67
 
58
68
  # Clipboard for copy/paste
59
69
  self._clipboard: Dict[int, Surface] = {}
@@ -64,6 +74,11 @@ class SpriteEditor:
64
74
  self._scale_text: str = "2.0"
65
75
  self._scale_error: Optional[str] = None
66
76
 
77
+ # Grid dialog state
78
+ self._grid_active: bool = False
79
+ self._grid_text: str = f"{tile_size[0]}x{tile_size[1]}"
80
+ self._grid_error: Optional[str] = None
81
+
67
82
  # Save path — never auto-overwrite original
68
83
  self._save_path: Optional[Path] = None
69
84
 
@@ -81,13 +96,19 @@ class SpriteEditor:
81
96
  ty = self.rect.y + (TOOLBAR_H - BTN_H) // 2
82
97
  gap = 6
83
98
  x = self.rect.x + gap
84
- labels = ["Flip X", "Flip Y", "Copy", "Paste", "Undo", "Redo", "Scale", "Save"]
99
+ labels = ["Flip X", "Flip Y", "Copy", "Paste", "Undo", "Redo", "Scale", "Grid", "Open", "Save"]
85
100
  for lbl in labels:
86
101
  self._btn_rects[lbl] = Rect(x, ty, BTN_W, BTN_H)
87
102
  x += BTN_W + gap
88
103
 
89
104
  def _get_status_text(self) -> str:
90
- parts = [f"Zoom: {self.grid.zoom:.1f}x"]
105
+ n = len(self._sheet_surfaces)
106
+ if n == 0:
107
+ return " No spritesheets loaded — Click [Open] to load one or more sheets"
108
+ tw, th = self.grid.tile_size
109
+ parts = [f"Tile: {tw}×{th} Zoom: {self.grid.zoom:.1f}x"]
110
+ if n > 1:
111
+ parts.append(f"Sheets: {n}")
91
112
  sel = len(self.grid.selected_indices)
92
113
  if sel:
93
114
  parts.append(f"Selected: {sel}")
@@ -109,6 +130,9 @@ class SpriteEditor:
109
130
  if self._scale_active:
110
131
  return self._handle_scale_dialog(event)
111
132
 
133
+ if self._grid_active:
134
+ return self._handle_grid_dialog(event)
135
+
112
136
  mouse = pygame.mouse.get_pos()
113
137
 
114
138
  # Keyboard shortcuts
@@ -182,6 +206,15 @@ class SpriteEditor:
182
206
  self._scale_text = "2.0"
183
207
  self._scale_error = None
184
208
  return True
209
+ elif lbl == "Grid":
210
+ self._grid_active = True
211
+ tw, th = self.grid.tile_size
212
+ self._grid_text = f"{tw}x{th}"
213
+ self._grid_error = None
214
+ return True
215
+ elif lbl == "Open":
216
+ self._open_add_sheets_dialog()
217
+ return True
185
218
  elif lbl == "Save":
186
219
  self._open_save_dialog()
187
220
  return True
@@ -213,6 +246,44 @@ class SpriteEditor:
213
246
  return True
214
247
  return False
215
248
 
249
+ def _handle_grid_dialog(self, event: pygame.event.Event) -> bool:
250
+ if event.type == pygame.KEYDOWN:
251
+ if event.key in (pygame.K_RETURN, pygame.K_KP_ENTER):
252
+ parts = self._grid_text.lower().split("x")
253
+ try:
254
+ tw = int(parts[0])
255
+ th = int(parts[1]) if len(parts) > 1 else tw
256
+ if tw < 1 or th < 1:
257
+ self._grid_error = "Must be >= 1"
258
+ else:
259
+ self.grid.set_tile_size(tw, th)
260
+ self._grid_active = False
261
+ self._grid_error = None
262
+ except ValueError:
263
+ self._grid_error = "Use format: WxH or N"
264
+ return True
265
+ elif event.key == pygame.K_ESCAPE:
266
+ self._grid_active = False
267
+ self._grid_error = None
268
+ return True
269
+ elif event.key == pygame.K_BACKSPACE:
270
+ self._grid_text = self._grid_text[:-1]
271
+ return True
272
+ elif event.unicode and event.unicode in "0123456789xX" and len(self._grid_text) < 10:
273
+ self._grid_text += event.unicode
274
+ return True
275
+ return False
276
+
277
+ @staticmethod
278
+ def _detect_tile_size(surface: Surface) -> tuple[int, int]:
279
+ w, h = surface.get_size()
280
+ candidates = [8, 12, 16, 24, 32, 48, 64, 128]
281
+ best = 8
282
+ for s in candidates:
283
+ if w % s == 0 and h % s == 0:
284
+ best = s
285
+ return (best, best)
286
+
216
287
  def _open_save_dialog(self) -> None:
217
288
  """Open FileManager in save mode to pick a PNG path."""
218
289
  from widgets.filemanager import FileManager
@@ -253,6 +324,64 @@ class SpriteEditor:
253
324
  def set_save_path(self, path: Path) -> None:
254
325
  self._save_path = path
255
326
 
327
+ # ------------------------------------------------------------------
328
+ # Multi-sheet: stack sheets vertically into one combined surface
329
+ # ------------------------------------------------------------------
330
+
331
+ def _build_combined_surface(self) -> None:
332
+ """Rebuild the combined surface from all loaded sheets (stacked vertically)."""
333
+ n = len(self._sheet_surfaces)
334
+ if n == 0:
335
+ return
336
+ if n == 1:
337
+ self.grid.set_surface(self._sheet_surfaces[0])
338
+ return
339
+
340
+ max_w = max(s.get_width() for s in self._sheet_surfaces)
341
+ total_h = sum(s.get_height() for s in self._sheet_surfaces)
342
+ combined = Surface((max_w, total_h), SRCALPHA)
343
+ combined.fill((0, 0, 0, 0))
344
+ y = 0
345
+ for surf in self._sheet_surfaces:
346
+ combined.blit(surf, (0, y))
347
+ y += surf.get_height()
348
+ self.grid.set_surface(combined)
349
+
350
+ def _open_add_sheets_dialog(self) -> None:
351
+ """Open FileManager in multi-select mode to pick one or more sheets."""
352
+ from widgets.filemanager import FileManager
353
+
354
+ fm_rect = self._file_manager_rect()
355
+ self._file_manager = FileManager(
356
+ rect=fm_rect,
357
+ initial_dir=self._data_root,
358
+ allowed_exts=[".png", ".jpg", ".jpeg", ".bmp", ".gif"],
359
+ on_select=self._on_add_sheets,
360
+ mode="open",
361
+ on_cancel=self._close_file_manager,
362
+ multi_select=True,
363
+ data_root=self._data_root,
364
+ )
365
+
366
+ def _on_add_sheets(self, selection: Union[Path, List[Path]]) -> None:
367
+ if isinstance(selection, Path):
368
+ selection = [selection]
369
+ was_empty = len(self._sheet_surfaces) == 0
370
+ loaded = 0
371
+ for path in selection:
372
+ try:
373
+ surf = pygame.image.load(str(path)).convert_alpha()
374
+ self._sheet_surfaces.append(surf)
375
+ loaded += 1
376
+ except Exception as e:
377
+ print(f"Failed to load {path.name}: {e}")
378
+ if loaded:
379
+ if was_empty:
380
+ detected = self._detect_tile_size(self._sheet_surfaces[0])
381
+ self.grid.set_tile_size(*detected)
382
+ self._build_combined_surface()
383
+ self._close_file_manager()
384
+
256
385
  # ------------------------------------------------------------------
257
386
  # Drawing
258
387
  # ------------------------------------------------------------------
@@ -295,6 +424,8 @@ class SpriteEditor:
295
424
 
296
425
  if self._scale_active:
297
426
  self._draw_scale_dialog(screen)
427
+ if self._grid_active:
428
+ self._draw_grid_dialog(screen)
298
429
 
299
430
  # File manager overlay
300
431
  if self._file_manager is not None:
@@ -321,3 +452,25 @@ class SpriteEditor:
321
452
 
322
453
  hint = self._font_sm.render("Enter / Esc", True, COLORS.text_dim)
323
454
  screen.blit(hint, (dlg.right - 90, dlg.bottom - 22))
455
+
456
+ def _draw_grid_dialog(self, screen: Surface) -> None:
457
+ dw, dh = 260, 100
458
+ cx, cy = self.rect.center
459
+ dlg = Rect(cx - dw // 2, cy - dh // 2, dw, dh)
460
+ draw_panel(screen, dlg, COLORS.panel, COLORS.border)
461
+
462
+ title = self._font.render("Tile Size (WxH)", True, COLORS.text)
463
+ screen.blit(title, title.get_rect(centerx=dlg.centerx, top=dlg.top + 10))
464
+
465
+ inp = Rect(dlg.x + 20, dlg.top + 36, dw - 40, 28)
466
+ pygame.draw.rect(screen, COLORS.selected, inp, border_radius=4)
467
+ pygame.draw.rect(screen, COLORS.accent, inp, 2, border_radius=4)
468
+ txt = self._grid_text + ("|" if (pygame.time.get_ticks() // 400) % 2 else "")
469
+ screen.blit(self._font_sm.render(txt, True, COLORS.text), (inp.x + 6, inp.y + 5))
470
+
471
+ if self._grid_error:
472
+ err = self._font_sm.render(self._grid_error, True, COLORS.danger)
473
+ screen.blit(err, (dlg.x + 20, dlg.bottom - 22))
474
+
475
+ hint = self._font_sm.render("Enter / Esc", True, COLORS.text_dim)
476
+ screen.blit(hint, (dlg.right - 90, dlg.bottom - 22))
@@ -2,9 +2,9 @@
2
2
  Standalone launcher for the Sprite Editor.
3
3
 
4
4
  Usage:
5
+ python standalone.py # start blank, then click [Open]
5
6
  python standalone.py path/to/spritesheet.png
6
7
  python standalone.py path/to/spritesheet.png --tile-size 32
7
- python standalone.py path/to/spritesheet.png --save output.png
8
8
  """
9
9
 
10
10
  from __future__ import annotations
@@ -47,7 +47,8 @@ def main(argv: list[str] | None = None) -> None:
47
47
  sys.excepthook = handle_exception
48
48
 
49
49
  parser = argparse.ArgumentParser(description="Grid Sprite Editor")
50
- parser.add_argument("image", type=str, help="Path to spritesheet image")
50
+ parser.add_argument("image", type=str, nargs="?", default=None,
51
+ help="Path to spritesheet image (optional — start blank)")
51
52
  parser.add_argument("--tile-size", type=str, default="32",
52
53
  help="Tile size (e.g. 32 or 32x32)")
53
54
  parser.add_argument("--save", type=str, default=None,
@@ -59,30 +60,35 @@ def main(argv: list[str] | None = None) -> None:
59
60
 
60
61
  args = parser.parse_args(argv)
61
62
 
62
- image_path = Path(args.image)
63
- if not image_path.is_file():
64
- print(f"Error: file not found: {image_path}")
65
- sys.exit(1)
66
-
67
63
  ts_parts = args.tile_size.lower().split("x")
68
64
  tw = int(ts_parts[0])
69
65
  th = int(ts_parts[1]) if len(ts_parts) > 1 else tw
70
66
 
71
67
  window_size = parse_window_size(args.window_size)
72
- data_root = Path(args.data_root) if args.data_root else image_path.parent
73
68
 
74
69
  pygame.init()
75
70
  screen = pygame.display.set_mode(window_size, pygame.RESIZABLE)
76
- pygame.display.set_caption(f"Sprite Editor - {image_path.name}")
77
-
78
- surface = pygame.image.load(str(image_path)).convert_alpha()
79
71
 
80
- editor = SpriteEditor(
81
- screen.get_rect(), surface, (tw, th),
82
- image_path=image_path, data_root=data_root,
83
- )
84
- if args.save:
85
- editor.set_save_path(Path(args.save))
72
+ if args.image:
73
+ image_path = Path(args.image)
74
+ if not image_path.is_file():
75
+ print(f"Error: file not found: {image_path}")
76
+ sys.exit(1)
77
+ data_root = Path(args.data_root) if args.data_root else image_path.parent
78
+ surface = pygame.image.load(str(image_path)).convert_alpha()
79
+ pygame.display.set_caption(f"Sprite Editor - {image_path.name}")
80
+ editor = SpriteEditor(
81
+ screen.get_rect(), surface, (tw, th),
82
+ image_path=image_path, data_root=data_root,
83
+ )
84
+ if args.save:
85
+ editor.set_save_path(Path(args.save))
86
+ else:
87
+ data_root = Path(args.data_root) if args.data_root else Path.cwd()
88
+ pygame.display.set_caption("Sprite Editor — blank")
89
+ editor = SpriteEditor(
90
+ screen.get_rect(), tile_size=(tw, th), data_root=data_root,
91
+ )
86
92
 
87
93
  clock = pygame.time.Clock()
88
94
  running = True
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tilemap-editor
3
- Version: 3.2.10
3
+ Version: 3.3.1
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
@@ -3,6 +3,8 @@ constants
3
3
  editor
4
4
  event_map
5
5
  layers
6
+ node_manager
7
+ nodes
6
8
  plugins
7
9
  standalone_automap
8
10
  standalone_error_console
@@ -503,6 +503,7 @@ class FileManager:
503
503
 
504
504
  self.font_main = pygame.font.SysFont("Arial", 14)
505
505
  self.font_bold = pygame.font.SysFont("Arial", 14, bold=True)
506
+ self.font_small = pygame.font.SysFont("Arial", 11)
506
507
  self.font_icon = pygame.font.SysFont("Consolas", 20)
507
508
 
508
509
  self.search_input = InlineTextInput("search", "")
@@ -1606,13 +1607,21 @@ class FileManager:
1606
1607
  pygame.draw.rect(screen, COLORS["border"], bar_rect, border_radius=2)
1607
1608
 
1608
1609
  def _draw_footer(self, screen, rect):
1609
- sel_txt = "No file selected"
1610
- if self.selected_index != -1:
1610
+ sel_txt: str
1611
+ if self.multi_select and len(self.selected_indices) > 1:
1612
+ sel_txt = f"{len(self.selected_indices)} files selected"
1613
+ elif self.selected_index != -1:
1611
1614
  sel_txt = self.items[self.selected_index].name
1615
+ else:
1616
+ sel_txt = "No file selected"
1612
1617
 
1613
- txt_surf = self.font_main.render(sel_txt, True, COLORS["text_dim"])
1614
1618
  if self.mode == "open":
1615
- screen.blit(txt_surf, (rect.x + 10, rect.y + 17))
1619
+ txt_surf = self.font_main.render(sel_txt, True, COLORS["text_dim"])
1620
+ screen.blit(txt_surf, (rect.x + 10, rect.y + 12))
1621
+ if self.multi_select:
1622
+ hint = "[Ctrl+Click] toggle [Shift+Click] range [Open] confirm"
1623
+ hint_surf = self.font_small.render(hint, True, COLORS["text_dim"])
1624
+ screen.blit(hint_surf, (rect.x + 10, rect.y + 28))
1616
1625
 
1617
1626
  btn_w, btn_h = 80, 30
1618
1627
  margin = 10
@@ -1449,10 +1449,11 @@ class TileGrid:
1449
1449
 
1450
1450
  sel_rect = Rect(int(sx), int(sy), int(sw), int(sh))
1451
1451
 
1452
- # Semi-transparent fill
1453
- fill_surf = pygame.Surface((int(sw), int(sh)), pygame.SRCALPHA)
1454
- fill_surf.fill((100, 180, 255, 40))
1455
- screen.blit(fill_surf, (int(sx), int(sy)))
1452
+ # Semi-transparent fill (skip during drag to avoid visual noise)
1453
+ if not self.is_selecting:
1454
+ fill_surf = pygame.Surface((int(sw), int(sh)), pygame.SRCALPHA)
1455
+ fill_surf.fill((100, 180, 255, 40))
1456
+ screen.blit(fill_surf, (int(sx), int(sy)))
1456
1457
 
1457
1458
  # Dashed border (draw segments)
1458
1459
  border_color = (100, 180, 255) if not self.is_moving else (255, 200, 50)
@@ -76,6 +76,11 @@ class MenuBar:
76
76
  MenuAction(
77
77
  "Toggle Auto-Autotile", self.editor.toggle_auto_autotile
78
78
  ),
79
+ MenuAction(
80
+ "Export Selection as PNG",
81
+ self.editor.export_selection_as_png,
82
+ "Ctrl+Shift+E",
83
+ ),
79
84
  MenuAction(
80
85
  "Launch External Viewer", self.editor.launch_external_automap
81
86
  ),
@@ -71,7 +71,7 @@ class TilesetTypeDialog:
71
71
  """Position child controls from the current dialog rect."""
72
72
  self.rect.center = self.editor_rect.center
73
73
 
74
- h = 220
74
+ h = 260
75
75
  if self.animated or self._editing_field is not None:
76
76
  h = 420
77
77
  self.rect.h = h
@@ -97,7 +97,7 @@ class TilesetTypeDialog:
97
97
  )
98
98
 
99
99
  # Animated checkbox
100
- anim_y = first_y + (row_h + gap) * 2 + 6
100
+ anim_y = first_y + (row_h + gap) * 2 + 16
101
101
  self.anim_check_rect = Rect(radio_x, anim_y, 16, 16)
102
102
 
103
103
  # Animation fields (collapsible)
File without changes