tilemap-editor 3.2.9__tar.gz → 3.3.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 (198) hide show
  1. {tilemap_editor-3.2.9/src/tilemap_editor.egg-info → tilemap_editor-3.3.0}/PKG-INFO +1 -1
  2. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/pyproject.toml +3 -1
  3. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/editor.py +2 -11
  4. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/sprite_editor/editor.py +161 -8
  5. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/sprite_editor/standalone.py +23 -17
  6. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0/src/tilemap_editor.egg-info}/PKG-INFO +1 -1
  7. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor.egg-info/top_level.txt +2 -0
  8. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/widgets/filemanager.py +13 -4
  9. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/widgets/tile_selector.py +54 -8
  10. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/LICENSE +0 -0
  11. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/MANIFEST.in +0 -0
  12. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/README.md +0 -0
  13. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
  14. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
  15. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
  16. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
  17. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
  18. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
  19. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
  20. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
  21. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/icons/arrow-down.svg +0 -0
  22. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/icons/check.svg +0 -0
  23. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/icons/checked.svg +0 -0
  24. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/icons/close.svg +0 -0
  25. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/icons/duplicate.svg +0 -0
  26. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/icons/error.svg +0 -0
  27. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/icons/file.svg +0 -0
  28. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/icons/filedead.svg +0 -0
  29. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/icons/fit.svg +0 -0
  30. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/icons/folder.svg +0 -0
  31. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/icons/grid.svg +0 -0
  32. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/icons/image.svg +0 -0
  33. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/icons/info.svg +0 -0
  34. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/icons/load.svg +0 -0
  35. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/icons/loop.svg +0 -0
  36. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/icons/pan.svg +0 -0
  37. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/icons/pause.svg +0 -0
  38. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/icons/pencil.svg +0 -0
  39. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/icons/play.svg +0 -0
  40. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/icons/plus.svg +0 -0
  41. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/icons/radio.svg +0 -0
  42. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/icons/reset.svg +0 -0
  43. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/icons/save.svg +0 -0
  44. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/icons/stop.svg +0 -0
  45. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/icons/tilemap.svg +0 -0
  46. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/icons/tileset.svg +0 -0
  47. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/icons/unchecked.svg +0 -0
  48. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/icons/warning.svg +0 -0
  49. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/icons/zoomin.svg +0 -0
  50. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/assets/icons/zoomout.svg +0 -0
  51. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/setup.cfg +0 -0
  52. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/configs/themes.py +0 -0
  53. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/constants.py +0 -0
  54. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/event_map.py +0 -0
  55. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/layers.py +0 -0
  56. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/main.py +0 -0
  57. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/node_manager.py +0 -0
  58. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/nodes.py +0 -0
  59. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/__init__.py +0 -0
  60. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/character_collision/__init__.py +0 -0
  61. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/character_collision/editor.py +0 -0
  62. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/character_collision/models.py +0 -0
  63. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/character_collision/protocols.py +0 -0
  64. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/character_collision/shape_editor.py +0 -0
  65. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/character_collision/standalone.py +0 -0
  66. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/object_tileset_collision/__init__.py +0 -0
  67. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/object_tileset_collision/editor.py +0 -0
  68. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/object_tileset_collision/models.py +0 -0
  69. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/object_tileset_collision/protocols.py +0 -0
  70. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/object_tileset_collision/standalone.py +0 -0
  71. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/sprite_animation/__init__.py +0 -0
  72. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/sprite_animation/__main__.py +0 -0
  73. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/sprite_animation/clipboard_util.py +0 -0
  74. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/sprite_animation/editor.py +0 -0
  75. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/sprite_animation/frame_picker.py +0 -0
  76. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/sprite_animation/models.py +0 -0
  77. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/sprite_animation/preview.py +0 -0
  78. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/sprite_animation/protocols.py +0 -0
  79. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/sprite_animation/runtime_load.py +0 -0
  80. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/sprite_animation/standalone.py +0 -0
  81. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/sprite_animation/timeline.py +0 -0
  82. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/sprite_animation/validation.py +0 -0
  83. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/sprite_editor/__init__.py +0 -0
  84. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/tileset_collision/__init__.py +0 -0
  85. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/tileset_collision/collision_painter.py +0 -0
  86. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/tileset_collision/editor.py +0 -0
  87. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/tileset_collision/models.py +0 -0
  88. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/tileset_collision/protocols.py +0 -0
  89. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/plugins/tileset_collision/standalone.py +0 -0
  90. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/standalone_automap.py +0 -0
  91. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/standalone_error_console.py +0 -0
  92. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/standalone_filemanager.py +0 -0
  93. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/standalone_image_viewer.py +0 -0
  94. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap.py +0 -0
  95. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/__init__.py +0 -0
  96. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/__main__.py +0 -0
  97. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/__init__.py +0 -0
  98. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
  99. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
  100. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
  101. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
  102. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
  103. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
  104. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
  105. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
  106. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/icons/ToolMove.svg +0 -0
  107. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/icons/arrow-down.svg +0 -0
  108. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/icons/check.svg +0 -0
  109. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/icons/checked.svg +0 -0
  110. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/icons/close.svg +0 -0
  111. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/icons/duplicate.svg +0 -0
  112. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/icons/error.svg +0 -0
  113. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/icons/file.svg +0 -0
  114. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/icons/filedead.svg +0 -0
  115. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/icons/fit.svg +0 -0
  116. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/icons/folder.svg +0 -0
  117. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/icons/grid.svg +0 -0
  118. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/icons/image.svg +0 -0
  119. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/icons/info.svg +0 -0
  120. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/icons/load.svg +0 -0
  121. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/icons/loop.svg +0 -0
  122. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/icons/pan.svg +0 -0
  123. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/icons/pause.svg +0 -0
  124. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/icons/pencil.svg +0 -0
  125. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/icons/play.svg +0 -0
  126. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/icons/plus.svg +0 -0
  127. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/icons/radio.svg +0 -0
  128. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/icons/reset.svg +0 -0
  129. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/icons/save.svg +0 -0
  130. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/icons/stop.svg +0 -0
  131. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/icons/tilemap.svg +0 -0
  132. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/icons/tileset.svg +0 -0
  133. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/icons/unchecked.svg +0 -0
  134. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/icons/warning.svg +0 -0
  135. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/icons/zoomin.svg +0 -0
  136. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/assets/icons/zoomout.svg +0 -0
  137. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/cli.py +0 -0
  138. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/main.py +0 -0
  139. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor/settings.py +0 -0
  140. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor.egg-info/SOURCES.txt +0 -0
  141. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor.egg-info/dependency_links.txt +0 -0
  142. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor.egg-info/entry_points.txt +0 -0
  143. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/tilemap_editor.egg-info/requires.txt +0 -0
  144. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/ttypes/__init__.py +0 -0
  145. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/ttypes/tilemap.py +0 -0
  146. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/utils/__init__.py +0 -0
  147. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/utils/editor_preference.py +0 -0
  148. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/utils/error_handler.py +0 -0
  149. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/utils/font_manager.py +0 -0
  150. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/utils/history.py +0 -0
  151. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/utils/icon_manager.py +0 -0
  152. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/utils/icons_cache.py +0 -0
  153. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/utils/log_capture.py +0 -0
  154. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/utils/project_paths.py +0 -0
  155. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/utils/serialization.py +0 -0
  156. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/utils/standalone.py +0 -0
  157. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/utils/validation.py +0 -0
  158. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/widgets/__init__.py +0 -0
  159. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/widgets/automap_models.py +0 -0
  160. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/widgets/autotile_template.py +0 -0
  161. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/widgets/autotiler.py +0 -0
  162. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/widgets/input.py +0 -0
  163. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/widgets/layer_selector.py +0 -0
  164. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/widgets/map_properties.py +0 -0
  165. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/widgets/mapsetup.py +0 -0
  166. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/widgets/regex_automap_designer.py +0 -0
  167. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/widgets/spritesheet_grid.py +0 -0
  168. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/widgets/tile_grid.py +0 -0
  169. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/widgets/ui/collision_layer_mask.py +0 -0
  170. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/widgets/ui/collision_layer_sidebar.py +0 -0
  171. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/widgets/ui/confirm_dialog.py +0 -0
  172. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/widgets/ui/draw_utils.py +0 -0
  173. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/widgets/ui/fileinput.py +0 -0
  174. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/widgets/ui/layer_type_dialog.py +0 -0
  175. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/widgets/ui/menubar.py +0 -0
  176. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/widgets/ui/mode_indicator.py +0 -0
  177. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/widgets/ui/node_editor.py +0 -0
  178. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/widgets/ui/node_selector.py +0 -0
  179. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/widgets/ui/notification.py +0 -0
  180. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/widgets/ui/property_editor.py +0 -0
  181. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/widgets/ui/region_selector.py +0 -0
  182. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/widgets/ui/status_bar.py +0 -0
  183. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/widgets/ui/theme.py +0 -0
  184. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/widgets/ui/tileset_type_dialog.py +0 -0
  185. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/widgets/ui/toolbar.py +0 -0
  186. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/src/widgets/ui/tooltip.py +0 -0
  187. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/tests/test_animation_library_grid_offset.py +0 -0
  188. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/tests/test_collision_layer_mask.py +0 -0
  189. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/tests/test_collision_layer_sidebar.py +0 -0
  190. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/tests/test_editor_pan_mode.py +0 -0
  191. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/tests/test_project_paths.py +0 -0
  192. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/tests/test_render_scale.py +0 -0
  193. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/tests/test_sprite_animation_editor_grid.py +0 -0
  194. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/tests/test_tile_grid_selection.py +0 -0
  195. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/tests/test_tile_selector_pick.py +0 -0
  196. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/tests/test_tilemap_save.py +0 -0
  197. {tilemap_editor-3.2.9 → tilemap_editor-3.3.0}/tests/test_timeline_invalidate_cache.py +0 -0
  198. {tilemap_editor-3.2.9 → tilemap_editor-3.3.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.2.9
3
+ Version: 3.3.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.2.9"
7
+ version = "3.3.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"
@@ -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",
@@ -771,23 +771,14 @@ class Editor:
771
771
  error_handler.capture(e, context="launch_animation_editor")
772
772
 
773
773
  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."""
774
+ """Launch the sprite editor (blank) in a new window. Use [Open] inside to load sheets."""
784
775
  try:
785
776
  tile_size = "32x32"
786
777
  if hasattr(self.tilemap, "tile_size") and self.tilemap.tile_size:
787
778
  tw, th = self.tilemap.tile_size
788
779
  tile_size = f"{tw}x{th}"
789
780
 
790
- args = [str(path), "--tile-size", tile_size, "--data-root", str(self.data_root)]
781
+ args = ["--tile-size", tile_size, "--data-root", str(self.data_root)]
791
782
  process = launch_standalone(
792
783
  "plugins.sprite_editor.standalone",
793
784
  args,
@@ -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.9
3
+ Version: 3.3.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
@@ -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
@@ -46,6 +46,10 @@ class TileSelector:
46
46
  x, y + self.top_bar_h, w, h - self.top_bar_h - self.btm_bar_h
47
47
  )
48
48
  self.tab_scroll_x = 0
49
+ arrow_h = self.top_bar_h - 4
50
+ arrow_y = y + (self.top_bar_h - arrow_h) // 2
51
+ self._tab_arrow_left = Rect(x + 2, arrow_y, 18, arrow_h)
52
+ self._tab_arrow_right = Rect(x + w - 20, arrow_y, 18, arrow_h)
49
53
  self.is_panning = False
50
54
  self.pan_start = (0, 0)
51
55
  self.pan_start_offset = (0, 0)
@@ -86,6 +90,10 @@ class TileSelector:
86
90
  self.btn_collision = Rect(x + w - 105, btn_y, 30, 30)
87
91
  self.btn_add = Rect(x + w - 70, btn_y, 30, 30)
88
92
  self.btn_rem = Rect(x + w - 35, btn_y, 30, 30)
93
+ arrow_h = self.top_bar_h - 4
94
+ arrow_y = y + (self.top_bar_h - arrow_h) // 2
95
+ self._tab_arrow_left = Rect(x + 2, arrow_y, 18, arrow_h)
96
+ self._tab_arrow_right = Rect(x + w - 20, arrow_y, 18, arrow_h)
89
97
 
90
98
  def set_rule_hints(self, hints: Set[int]):
91
99
  self.rule_hints = hints
@@ -179,6 +187,13 @@ class TileSelector:
179
187
  return True
180
188
 
181
189
  if self.rect.collidepoint(mouse_pos) and mouse_pos[1] < self.view_rect.top:
190
+ # Check arrow buttons first
191
+ if self._tab_arrow_left.collidepoint(mouse_pos):
192
+ self._scroll_tabs(100)
193
+ return True
194
+ if self._tab_arrow_right.collidepoint(mouse_pos):
195
+ self._scroll_tabs(-100)
196
+ return True
182
197
  self.check_tab_click(mouse_pos)
183
198
  return True
184
199
 
@@ -561,8 +576,8 @@ class TileSelector:
561
576
  def _get_tab_at_pos(self, pos) -> Optional[int]:
562
577
  if not self.tilesets:
563
578
  return None
564
- tab_w = min(100, self.rect.width // len(self.tilesets))
565
- idx = (pos[0] - self.rect.x) // tab_w
579
+ tab_w = 100
580
+ idx = (pos[0] - self.rect.x + self.tab_scroll_x) // tab_w
566
581
  if 0 <= idx < len(self.tilesets):
567
582
  return int(idx)
568
583
  return None
@@ -875,14 +890,19 @@ class TileSelector:
875
890
  clip = screen.get_clip()
876
891
  tab_area = Rect(self.rect.x, self.rect.y, self.rect.width, self.top_bar_h)
877
892
  screen.set_clip(tab_area)
893
+
894
+ pygame.draw.rect(screen, COLORS.header, tab_area)
895
+
878
896
  tab_w = 100
879
897
  total_w = tab_w * len(self.tilesets)
880
- if total_w > self.rect.width:
898
+ has_overflow = total_w > self.rect.width
899
+ if has_overflow:
881
900
  self.tab_scroll_x = max(
882
901
  0, min(self.tab_scroll_x, total_w - self.rect.width)
883
902
  )
884
903
  else:
885
904
  self.tab_scroll_x = 0
905
+
886
906
  for i, ts in enumerate(self.tilesets):
887
907
  r = Rect(
888
908
  self.rect.x + i * tab_w - self.tab_scroll_x,
@@ -895,13 +915,25 @@ class TileSelector:
895
915
  is_active = i == self.active_idx
896
916
  col = COLORS.selected if is_active else COLORS.header
897
917
  pygame.draw.rect(screen, col, r)
898
- pygame.draw.rect(screen, COLORS.border, r, 1)
918
+ if not is_active:
919
+ pygame.draw.line(screen, COLORS.border, r.bottomleft, r.bottomright, 1)
899
920
  t = ts.name[:8] + ".." if len(ts.name) > 10 else ts.name
900
921
  screen.blit(self.font.render(t, True, COLORS.text), (r.x + 5, r.y + 5))
901
- mx, my = pygame.mouse.get_pos()
902
- if r.collidepoint(mx, my):
903
- self.editor.tooltip.show(ts.name, (mx + 10, my + 10))
904
- if total_w > self.rect.width:
922
+
923
+ mx, my = pygame.mouse.get_pos()
924
+ show_arrows = has_overflow and tab_area.collidepoint(mx, my)
925
+
926
+ if show_arrows:
927
+ for arrow_rect, label in [
928
+ (self._tab_arrow_left, "<"),
929
+ (self._tab_arrow_right, ">"),
930
+ ]:
931
+ pygame.draw.rect(screen, COLORS.selected, arrow_rect)
932
+ pygame.draw.rect(screen, COLORS.border, arrow_rect, 1)
933
+ arrow_surf = self.font.render(label, True, COLORS.text)
934
+ arrow_rect_text = arrow_surf.get_rect(center=arrow_rect.center)
935
+ screen.blit(arrow_surf, arrow_rect_text)
936
+
905
937
  bar_w = max(30, int(self.rect.width * (self.rect.width / total_w)))
906
938
  bar_x = self.rect.x + int(
907
939
  (self.tab_scroll_x / (total_w - self.rect.width))
@@ -909,6 +941,20 @@ class TileSelector:
909
941
  )
910
942
  bar_rect = Rect(bar_x, self.rect.y + self.top_bar_h - 4, bar_w, 3)
911
943
  pygame.draw.rect(screen, COLORS.border_soft, bar_rect, border_radius=2)
944
+
945
+ # Tooltip for tab name
946
+ if tab_area.collidepoint(mx, my) and not (
947
+ show_arrows and (
948
+ self._tab_arrow_left.collidepoint(mx, my) or
949
+ self._tab_arrow_right.collidepoint(mx, my)
950
+ )
951
+ ):
952
+ idx = self._get_tab_at_pos((mx, my))
953
+ if idx is not None and 0 <= idx < len(self.tilesets):
954
+ self.editor.tooltip.show(
955
+ self.tilesets[idx].name, (mx + 10, my + 10)
956
+ )
957
+
912
958
  screen.set_clip(clip)
913
959
 
914
960
  def _scroll_tabs(self, delta: float):
File without changes
File without changes