tilemap-editor 3.2.5__tar.gz → 3.2.7__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 (194) hide show
  1. {tilemap_editor-3.2.5/src/tilemap_editor.egg-info → tilemap_editor-3.2.7}/PKG-INFO +1 -1
  2. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/pyproject.toml +1 -1
  3. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/editor.py +13 -0
  4. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/character_collision/shape_editor.py +3 -3
  5. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/sprite_animation/editor.py +7 -0
  6. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/sprite_animation/preview.py +9 -2
  7. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7/src/tilemap_editor.egg-info}/PKG-INFO +1 -1
  8. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor.egg-info/SOURCES.txt +1 -0
  9. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/widgets/tile_grid.py +14 -11
  10. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/widgets/tile_selector.py +72 -46
  11. tilemap_editor-3.2.7/src/widgets/ui/confirm_dialog.py +149 -0
  12. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/LICENSE +0 -0
  13. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/MANIFEST.in +0 -0
  14. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/README.md +0 -0
  15. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
  16. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
  17. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
  18. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
  19. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
  20. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
  21. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
  22. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
  23. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/icons/arrow-down.svg +0 -0
  24. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/icons/check.svg +0 -0
  25. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/icons/checked.svg +0 -0
  26. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/icons/close.svg +0 -0
  27. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/icons/duplicate.svg +0 -0
  28. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/icons/error.svg +0 -0
  29. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/icons/file.svg +0 -0
  30. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/icons/filedead.svg +0 -0
  31. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/icons/fit.svg +0 -0
  32. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/icons/folder.svg +0 -0
  33. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/icons/grid.svg +0 -0
  34. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/icons/image.svg +0 -0
  35. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/icons/info.svg +0 -0
  36. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/icons/load.svg +0 -0
  37. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/icons/loop.svg +0 -0
  38. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/icons/pan.svg +0 -0
  39. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/icons/pause.svg +0 -0
  40. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/icons/pencil.svg +0 -0
  41. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/icons/play.svg +0 -0
  42. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/icons/plus.svg +0 -0
  43. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/icons/radio.svg +0 -0
  44. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/icons/reset.svg +0 -0
  45. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/icons/save.svg +0 -0
  46. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/icons/stop.svg +0 -0
  47. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/icons/tilemap.svg +0 -0
  48. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/icons/tileset.svg +0 -0
  49. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/icons/unchecked.svg +0 -0
  50. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/icons/warning.svg +0 -0
  51. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/icons/zoomin.svg +0 -0
  52. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/assets/icons/zoomout.svg +0 -0
  53. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/setup.cfg +0 -0
  54. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/configs/themes.py +0 -0
  55. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/constants.py +0 -0
  56. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/event_map.py +0 -0
  57. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/layers.py +0 -0
  58. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/main.py +0 -0
  59. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/__init__.py +0 -0
  60. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/character_collision/__init__.py +0 -0
  61. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/character_collision/editor.py +0 -0
  62. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/character_collision/models.py +0 -0
  63. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/character_collision/protocols.py +0 -0
  64. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/character_collision/standalone.py +0 -0
  65. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/object_tileset_collision/__init__.py +0 -0
  66. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/object_tileset_collision/editor.py +0 -0
  67. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/object_tileset_collision/models.py +0 -0
  68. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/object_tileset_collision/protocols.py +0 -0
  69. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/object_tileset_collision/standalone.py +0 -0
  70. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/sprite_animation/__init__.py +0 -0
  71. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/sprite_animation/__main__.py +0 -0
  72. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/sprite_animation/clipboard_util.py +0 -0
  73. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/sprite_animation/frame_picker.py +0 -0
  74. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/sprite_animation/models.py +0 -0
  75. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/sprite_animation/protocols.py +0 -0
  76. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/sprite_animation/runtime_load.py +0 -0
  77. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/sprite_animation/standalone.py +0 -0
  78. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/sprite_animation/timeline.py +0 -0
  79. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/sprite_animation/validation.py +0 -0
  80. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/sprite_editor/__init__.py +0 -0
  81. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/sprite_editor/editor.py +0 -0
  82. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/sprite_editor/standalone.py +0 -0
  83. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/tileset_collision/__init__.py +0 -0
  84. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/tileset_collision/collision_painter.py +0 -0
  85. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/tileset_collision/editor.py +0 -0
  86. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/tileset_collision/models.py +0 -0
  87. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/tileset_collision/protocols.py +0 -0
  88. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/plugins/tileset_collision/standalone.py +0 -0
  89. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/standalone_automap.py +0 -0
  90. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/standalone_error_console.py +0 -0
  91. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/standalone_filemanager.py +0 -0
  92. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/standalone_image_viewer.py +0 -0
  93. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap.py +0 -0
  94. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/__init__.py +0 -0
  95. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/__main__.py +0 -0
  96. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/__init__.py +0 -0
  97. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
  98. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
  99. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
  100. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
  101. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
  102. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
  103. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
  104. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
  105. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/icons/ToolMove.svg +0 -0
  106. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/icons/arrow-down.svg +0 -0
  107. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/icons/check.svg +0 -0
  108. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/icons/checked.svg +0 -0
  109. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/icons/close.svg +0 -0
  110. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/icons/duplicate.svg +0 -0
  111. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/icons/error.svg +0 -0
  112. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/icons/file.svg +0 -0
  113. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/icons/filedead.svg +0 -0
  114. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/icons/fit.svg +0 -0
  115. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/icons/folder.svg +0 -0
  116. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/icons/grid.svg +0 -0
  117. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/icons/image.svg +0 -0
  118. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/icons/info.svg +0 -0
  119. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/icons/load.svg +0 -0
  120. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/icons/loop.svg +0 -0
  121. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/icons/pan.svg +0 -0
  122. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/icons/pause.svg +0 -0
  123. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/icons/pencil.svg +0 -0
  124. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/icons/play.svg +0 -0
  125. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/icons/plus.svg +0 -0
  126. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/icons/radio.svg +0 -0
  127. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/icons/reset.svg +0 -0
  128. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/icons/save.svg +0 -0
  129. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/icons/stop.svg +0 -0
  130. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/icons/tilemap.svg +0 -0
  131. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/icons/tileset.svg +0 -0
  132. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/icons/unchecked.svg +0 -0
  133. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/icons/warning.svg +0 -0
  134. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/icons/zoomin.svg +0 -0
  135. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/assets/icons/zoomout.svg +0 -0
  136. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/cli.py +0 -0
  137. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/main.py +0 -0
  138. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor/settings.py +0 -0
  139. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor.egg-info/dependency_links.txt +0 -0
  140. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor.egg-info/entry_points.txt +0 -0
  141. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor.egg-info/requires.txt +0 -0
  142. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/tilemap_editor.egg-info/top_level.txt +0 -0
  143. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/ttypes/__init__.py +0 -0
  144. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/ttypes/tilemap.py +0 -0
  145. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/utils/__init__.py +0 -0
  146. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/utils/editor_preference.py +0 -0
  147. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/utils/error_handler.py +0 -0
  148. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/utils/font_manager.py +0 -0
  149. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/utils/history.py +0 -0
  150. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/utils/icon_manager.py +0 -0
  151. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/utils/icons_cache.py +0 -0
  152. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/utils/log_capture.py +0 -0
  153. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/utils/project_paths.py +0 -0
  154. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/utils/serialization.py +0 -0
  155. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/utils/standalone.py +0 -0
  156. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/utils/validation.py +0 -0
  157. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/widgets/__init__.py +0 -0
  158. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/widgets/automap_models.py +0 -0
  159. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/widgets/autotile_template.py +0 -0
  160. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/widgets/autotiler.py +0 -0
  161. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/widgets/filemanager.py +0 -0
  162. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/widgets/input.py +0 -0
  163. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/widgets/layer_selector.py +0 -0
  164. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/widgets/map_properties.py +0 -0
  165. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/widgets/mapsetup.py +0 -0
  166. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/widgets/regex_automap_designer.py +0 -0
  167. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/widgets/spritesheet_grid.py +0 -0
  168. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/widgets/ui/collision_layer_mask.py +0 -0
  169. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/widgets/ui/collision_layer_sidebar.py +0 -0
  170. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/widgets/ui/draw_utils.py +0 -0
  171. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/widgets/ui/fileinput.py +0 -0
  172. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/widgets/ui/layer_type_dialog.py +0 -0
  173. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/widgets/ui/menubar.py +0 -0
  174. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/widgets/ui/mode_indicator.py +0 -0
  175. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/widgets/ui/notification.py +0 -0
  176. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/widgets/ui/property_editor.py +0 -0
  177. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/widgets/ui/region_selector.py +0 -0
  178. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/widgets/ui/status_bar.py +0 -0
  179. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/widgets/ui/theme.py +0 -0
  180. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/widgets/ui/tileset_type_dialog.py +0 -0
  181. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/widgets/ui/toolbar.py +0 -0
  182. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/src/widgets/ui/tooltip.py +0 -0
  183. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/tests/test_animation_library_grid_offset.py +0 -0
  184. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/tests/test_collision_layer_mask.py +0 -0
  185. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/tests/test_collision_layer_sidebar.py +0 -0
  186. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/tests/test_editor_pan_mode.py +0 -0
  187. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/tests/test_project_paths.py +0 -0
  188. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/tests/test_render_scale.py +0 -0
  189. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/tests/test_sprite_animation_editor_grid.py +0 -0
  190. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/tests/test_tile_grid_selection.py +0 -0
  191. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/tests/test_tile_selector_pick.py +0 -0
  192. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/tests/test_tilemap_save.py +0 -0
  193. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/tests/test_timeline_invalidate_cache.py +0 -0
  194. {tilemap_editor-3.2.5 → tilemap_editor-3.2.7}/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.5
3
+ Version: 3.2.7
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.5"
7
+ version = "3.2.7"
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"
@@ -27,6 +27,7 @@ from widgets.tile_grid import TileGrid
27
27
  from widgets.layer_selector import LayerSelector
28
28
  from widgets.ui.fileinput import FilenameInput
29
29
  from widgets.ui.tileset_type_dialog import TilesetTypeDialog
30
+ from widgets.ui.confirm_dialog import ConfirmDialog
30
31
  from widgets.ui.layer_type_dialog import LayerTypeDialog
31
32
  from widgets.ui.menubar import MenuBar
32
33
  from widgets.ui.toolbar import Toolbar
@@ -208,6 +209,7 @@ class Editor:
208
209
  on_cancel=lambda: None,
209
210
  )
210
211
  self.tileset_type_dialog = TilesetTypeDialog(editor_rect)
212
+ self.confirm_dialog = ConfirmDialog(editor_rect)
211
213
  self.layer_type_dialog = LayerTypeDialog(editor_rect)
212
214
 
213
215
  self.menubar = MenuBar(self, self.width, 30)
@@ -542,6 +544,8 @@ class Editor:
542
544
 
543
545
  if hasattr(self, "tileset_type_dialog") and self.tileset_type_dialog:
544
546
  self.tileset_type_dialog.editor_rect = rect_full
547
+ if hasattr(self, "confirm_dialog") and self.confirm_dialog:
548
+ self.confirm_dialog.editor_rect = rect_full
545
549
  if hasattr(self, "layer_type_dialog") and self.layer_type_dialog:
546
550
  self.layer_type_dialog.editor_rect = rect_full
547
551
 
@@ -1052,6 +1056,10 @@ class Editor:
1052
1056
  self.tileset_type_dialog.handle_event(event)
1053
1057
  continue
1054
1058
 
1059
+ if self.confirm_dialog.active:
1060
+ self.confirm_dialog.handle_event(event)
1061
+ continue
1062
+
1055
1063
  if self.layer_type_dialog.active:
1056
1064
  self.layer_type_dialog.handle_event(event)
1057
1065
  continue
@@ -1261,6 +1269,11 @@ class Editor:
1261
1269
  overlay.fill((0, 0, 0, 150))
1262
1270
  self.screen.blit(overlay, (0, 0))
1263
1271
  self.tileset_type_dialog.draw(self.screen)
1272
+ if self.confirm_dialog.active:
1273
+ overlay = pygame.Surface((self.width, self.height), pygame.SRCALPHA)
1274
+ overlay.fill((0, 0, 0, 150))
1275
+ self.screen.blit(overlay, (0, 0))
1276
+ self.confirm_dialog.draw(self.screen)
1264
1277
  if self.layer_type_dialog.active:
1265
1278
  overlay = pygame.Surface((self.width, self.height), pygame.SRCALPHA)
1266
1279
  overlay.fill((0, 0, 0, 150))
@@ -294,8 +294,8 @@ class ShapeEditor:
294
294
  self._dragging_shape = False
295
295
  return True
296
296
 
297
- # Check if clicking on move icon
298
- if self._is_on_move_icon(mouse) and not self._pan_mode:
297
+ # Check if clicking inside the shape body (but not on a handle)
298
+ if self._is_inside_shape(mouse) and not self._pan_mode:
299
299
  self._dragging_shape = True
300
300
  self._drag_start = mouse
301
301
  self._save_shape_positions()
@@ -462,7 +462,7 @@ class ShapeEditor:
462
462
  elif self.shape_type == "circle":
463
463
  return self._sprite_to_screen((self.circle_x, self.circle_y - self.circle_radius))
464
464
  elif self.shape_type == "capsule":
465
- return self._sprite_to_screen((self.capsule_x, self.capsule_y))
465
+ return self._sprite_to_screen((self.capsule_x, self.capsule_y + self.capsule_height / 2))
466
466
  elif self.shape_type == "polygon" and len(self.polygon_vertices) >= 3:
467
467
  # Top-center of polygon bounding box
468
468
  min_y = min(v[1] for v in self.polygon_vertices)
@@ -168,6 +168,7 @@ class SpriteAnimationEditor:
168
168
  self.timeline.on_frames_changed = self._on_frames_changed
169
169
  self.timeline.on_markers_changed = self._on_markers_changed
170
170
  self.preview.on_playback_fps_changed = self._on_preview_fps_changed
171
+ self.preview.on_loop_changed = self._on_preview_loop_changed
171
172
 
172
173
  # Create a default animation (now safe — widgets exist)
173
174
  self._create_new_animation("idle")
@@ -1566,6 +1567,12 @@ class SpriteAnimationEditor:
1566
1567
  self.preview.authoring_fps = float(fps)
1567
1568
  self._notify_animation_modified()
1568
1569
 
1570
+ def _on_preview_loop_changed(self, loop: bool) -> None:
1571
+ anim = self._get_active()
1572
+ if anim:
1573
+ anim.loop = loop
1574
+ self._notify_animation_modified()
1575
+
1569
1576
  def _metadata_apply_add(self, anim: Animation) -> None:
1570
1577
  key = self._meta_key_input.strip()
1571
1578
  if not key:
@@ -58,10 +58,13 @@ class AnimationPreview:
58
58
  self.frames: List[AnimationFrame] = []
59
59
  self.playing = False
60
60
  self.loop = True
61
+ self.grid_offset_x: int = 0
62
+ self.grid_offset_y: int = 0
61
63
  self.playback_fps: float = 60.0
62
64
  self.show_onion = False
63
65
 
64
66
  self.on_playback_fps_changed: Optional[Callable[[float], None]] = None
67
+ self.on_loop_changed: Optional[Callable[[bool], None]] = None
65
68
  self.authoring_fps: float = 60.0
66
69
 
67
70
  self.current_frame: int = 0
@@ -221,6 +224,8 @@ class AnimationPreview:
221
224
  return True
222
225
  if self._btn_loop.collidepoint(mouse):
223
226
  self.loop = not self.loop
227
+ if self.on_loop_changed:
228
+ self.on_loop_changed(self.loop)
224
229
  return True
225
230
  if self._btn_onion.collidepoint(mouse):
226
231
  self.show_onion = not self.show_onion
@@ -411,10 +416,12 @@ class AnimationPreview:
411
416
 
412
417
  def _extract_tile(self, variant_id: int) -> Optional[pygame.Surface]:
413
418
  tw, th = self.tile_size
414
- cols = max(1, self.surface.get_width() // tw)
419
+ ox, oy = self.grid_offset_x, self.grid_offset_y
420
+ available_w = self.surface.get_width() - ox
421
+ cols = max(1, available_w // tw)
415
422
  col = variant_id % cols
416
423
  row = variant_id // cols
417
- src = Rect(col * tw, row * th, tw, th)
424
+ src = Rect(ox + col * tw, oy + row * th, tw, th)
418
425
  if self.surface.get_rect().contains(src):
419
426
  return self.surface.subsurface(src).copy()
420
427
  return None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tilemap-editor
3
- Version: 3.2.5
3
+ Version: 3.2.7
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
@@ -164,6 +164,7 @@ src/widgets/tile_grid.py
164
164
  src/widgets/tile_selector.py
165
165
  src/widgets/ui/collision_layer_mask.py
166
166
  src/widgets/ui/collision_layer_sidebar.py
167
+ src/widgets/ui/confirm_dialog.py
167
168
  src/widgets/ui/draw_utils.py
168
169
  src/widgets/ui/fileinput.py
169
170
  src/widgets/ui/layer_type_dialog.py
@@ -499,8 +499,10 @@ class TileGrid:
499
499
  start_sy = src_rect[1] // tile_h
500
500
 
501
501
  if active_layer.layer_type == "object":
502
- pixel_x = self.hover_cell[0] * tile_w
503
- pixel_y = self.hover_cell[1] * tile_h
502
+ mouse_pos = pygame.mouse.get_pos()
503
+ world_x, world_y = self.screen_to_world(mouse_pos)
504
+ pixel_x = world_x
505
+ pixel_y = world_y
504
506
  pixel_w = sel_w_tiles * tile_w
505
507
  pixel_h = sel_h_tiles * tile_h
506
508
 
@@ -1040,13 +1042,14 @@ class TileGrid:
1040
1042
 
1041
1043
  if active_layer.layer_type == "object":
1042
1044
  mouse_pos = pygame.mouse.get_pos()
1043
- self.screen_to_world(mouse_pos)
1045
+ world_x, world_y = self.screen_to_world(mouse_pos)
1046
+ rs = self.editor.tilemap.render_scale
1044
1047
 
1045
- sel_width = int(src_rect[2] * self.zoom_level)
1046
- sel_height = int(src_rect[3] * self.zoom_level)
1048
+ sel_width = int(src_rect[2] * rs * self.zoom_level)
1049
+ sel_height = int(src_rect[3] * rs * self.zoom_level)
1047
1050
 
1048
- screen_x = mouse_pos[0]
1049
- screen_y = mouse_pos[1]
1051
+ screen_x = (world_x - self.scroll_x) * self.zoom_level + self.rect.x
1052
+ screen_y = (world_y - self.scroll_y) * self.zoom_level + self.rect.y
1050
1053
 
1051
1054
  dest_rect = Rect(screen_x, screen_y, sel_width, sel_height)
1052
1055
  pygame.draw.rect(screen, (255, 255, 0), dest_rect, 2)
@@ -1336,12 +1339,12 @@ class TileGrid:
1336
1339
  src_rect = Rect(src_x, src_y, obj_w, obj_h)
1337
1340
 
1338
1341
  dest_x = (
1339
- (obj_x * rs - self.scroll_x) * self.zoom_level
1342
+ (obj_x - self.scroll_x) * self.zoom_level
1340
1343
  + self.rect.x
1341
1344
  + draw_offset_x
1342
1345
  )
1343
1346
  dest_y = (
1344
- (obj_y * rs - self.scroll_y) * self.zoom_level
1347
+ (obj_y - self.scroll_y) * self.zoom_level
1345
1348
  + self.rect.y
1346
1349
  + draw_offset_y
1347
1350
  )
@@ -1565,11 +1568,11 @@ class TileGrid:
1565
1568
  new_x = area["x"] + dx
1566
1569
  new_y = area["y"] + dy
1567
1570
  dest_x = (
1568
- (new_x * rs - self.scroll_x) * self.zoom_level
1571
+ (new_x - self.scroll_x) * self.zoom_level
1569
1572
  + self.rect.x
1570
1573
  )
1571
1574
  dest_y = (
1572
- (new_y * rs - self.scroll_y) * self.zoom_level
1575
+ (new_y - self.scroll_y) * self.zoom_level
1573
1576
  + self.rect.y
1574
1577
  )
1575
1578
 
@@ -61,6 +61,7 @@ class TileSelector:
61
61
  self.zoom: float = 1.0
62
62
 
63
63
  btn_y = y + h - 35
64
+ self.btn_export = Rect(x + w - 140, btn_y, 30, 30)
64
65
  self.btn_collision = Rect(x + w - 105, btn_y, 30, 30)
65
66
  self.btn_add = Rect(x + w - 70, btn_y, 30, 30)
66
67
  self.btn_rem = Rect(x + w - 35, btn_y, 30, 30)
@@ -80,6 +81,7 @@ class TileSelector:
80
81
  x, y + self.top_bar_h, w, h - self.top_bar_h - self.btm_bar_h
81
82
  )
82
83
  btn_y = y + h - 35
84
+ self.btn_export = Rect(x + w - 140, btn_y, 30, 30)
83
85
  self.btn_collision = Rect(x + w - 105, btn_y, 30, 30)
84
86
  self.btn_add = Rect(x + w - 70, btn_y, 30, 30)
85
87
  self.btn_rem = Rect(x + w - 35, btn_y, 30, 30)
@@ -162,6 +164,9 @@ class TileSelector:
162
164
  return True
163
165
 
164
166
  if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
167
+ if self.btn_export.collidepoint(mouse_pos):
168
+ self._export_selected_as_png_dialog()
169
+ return True
165
170
  if self.btn_collision.collidepoint(mouse_pos):
166
171
  self.open_collision_editor()
167
172
  return True
@@ -246,6 +251,11 @@ class TileSelector:
246
251
  ts.offset[1] += event.y * 20
247
252
  return True
248
253
 
254
+ elif event.type == pygame.KEYDOWN and event.key == pygame.K_e:
255
+ if self.selected_tile and self.active_idx != -1:
256
+ self._export_selected_as_png_dialog()
257
+ return True
258
+
249
259
  return False
250
260
 
251
261
  def update_selection_rect(self, current_grid: Tuple[int, int]):
@@ -348,66 +358,56 @@ class TileSelector:
348
358
  if path.exists():
349
359
  try:
350
360
  surf = pygame.image.load(path).convert_alpha()
351
- if tileset_type == "object" or is_image_multipleof(
352
- surf.get_size(), self.editor.tilemap.tile_size
353
- ):
354
- tileset_data = TilesetData(
355
- path.name, path, surf, tileset_type=tileset_type
356
- )
357
- tileset_data.properties = properties
358
- # Convert string keys back to int if necessary
359
- tileset_data.tile_properties = {
360
- int(k): v for k, v in tile_properties.items()
361
- }
362
- self._load_collision_data_for_tileset(tileset_data)
363
-
364
- self.tilesets.append(tileset_data)
365
- self.active_idx = len(self.tilesets) - 1
366
- self.tileset_map[self.active_idx] = tileset_data
367
- if tileset_type == "object":
368
- self.selected_tile = (0, 0, surf.get_width(), surf.get_height())
369
- else:
370
- print("Tileset isnt multiple of tile size")
361
+ tileset_data = TilesetData(
362
+ path.name, path, surf, tileset_type=tileset_type
363
+ )
364
+ tileset_data.properties = properties
365
+ tileset_data.tile_properties = {
366
+ int(k): v for k, v in tile_properties.items()
367
+ }
368
+ self._load_collision_data_for_tileset(tileset_data)
369
+
370
+ self.tilesets.append(tileset_data)
371
+ self.active_idx = len(self.tilesets) - 1
372
+ self.tileset_map[self.active_idx] = tileset_data
373
+ if tileset_type == "object":
374
+ self.selected_tile = (0, 0, surf.get_width(), surf.get_height())
371
375
  except Exception as e:
372
376
  error_handler.capture(e, context="load_tileset_surface")
373
377
 
374
378
  def _on_tileset_type_selected(self, tileset_type: str):
375
379
  """Callback when user selects tileset type from dialog."""
376
- print(f"DEBUG: Tileset type selected: {tileset_type}")
377
380
  if not hasattr(self, "_pending_tileset_path"):
378
- print("DEBUG: No pending tileset path!")
379
381
  return
380
382
 
381
- path = self._pending_tileset_path
382
383
  surf = self._pending_tileset_surf
383
384
 
384
385
  if tileset_type == "tile" and not is_image_multipleof(
385
386
  surf.get_size(), self.editor.tilemap.tile_size
386
387
  ):
387
- self.editor.notifications.error(
388
- "Tileset size must be multiple of tile size"
388
+ w, h = surf.get_size()
389
+ tw, th = self.editor.tilemap.tile_size
390
+ self.editor.confirm_dialog.show(
391
+ title="Tileset Size Warning",
392
+ message=f"Image size ({w}x{h}) is not an exact multiple of tile size ({tw}x{th}). "
393
+ f"Some pixels at the edges will be ignored. Proceed?",
394
+ on_confirm=lambda: self._commit_tileset_load(tileset_type),
395
+ on_cancel=self._on_tileset_type_cancel,
389
396
  )
390
- delattr(self, "_pending_tileset_path")
391
- delattr(self, "_pending_tileset_surf")
392
- if self._pending_tileset_queue:
393
- error_handler.capture(
394
- Exception(
395
- f"Queue error continuation: {len(self._pending_tileset_queue)} items"
396
- ),
397
- context="tileset_queue_error",
398
- severity="info",
399
- )
400
- # Delay slightly to allow dialog to fully close
401
- pygame.time.set_timer(pygame.USEREVENT + 1, 100, 1)
402
- self._queue_timer_active = True
403
397
  return
404
398
 
399
+ self._commit_tileset_load(tileset_type)
400
+
401
+ def _commit_tileset_load(self, tileset_type: str):
402
+ path = self._pending_tileset_path
403
+ surf = self._pending_tileset_surf
404
+
405
405
  tileset_data = TilesetData(path.name, path, surf, tileset_type=tileset_type)
406
406
  self._load_collision_data_for_tileset(tileset_data)
407
407
  self.tilesets.append(tileset_data)
408
408
  self.active_idx = len(self.tilesets) - 1
409
409
  self.tileset_map[self.active_idx] = tileset_data
410
- print(f"DEBUG: Added tileset {path.name}, total tilesets: {len(self.tilesets)}")
410
+
411
411
  if tileset_type == "object":
412
412
  self.selected_tile = (0, 0, surf.get_width(), surf.get_height())
413
413
  else:
@@ -416,14 +416,8 @@ class TileSelector:
416
416
  delattr(self, "_pending_tileset_path")
417
417
  delattr(self, "_pending_tileset_surf")
418
418
  if self._pending_tileset_queue:
419
- print(
420
- f"DEBUG: Continuing queue, {len(self._pending_tileset_queue)} items remaining"
421
- )
422
- # Delay slightly to allow dialog to fully close
423
419
  pygame.time.set_timer(pygame.USEREVENT + 1, 100, 1)
424
420
  self._queue_timer_active = True
425
- else:
426
- print("DEBUG: Queue processing complete")
427
421
 
428
422
  def _load_collision_data_for_tileset(self, tileset_data: TilesetData) -> None:
429
423
  """Attach existing collision data for tilesets that have sidecar files."""
@@ -631,6 +625,27 @@ class TileSelector:
631
625
  self.selected_tile = (col * tw, row * th, tw, th)
632
626
  return True
633
627
 
628
+ def _export_selected_as_png_dialog(self):
629
+ if not self.selected_tile or self.active_idx == -1:
630
+ return
631
+ ts = self.tilesets[self.active_idx]
632
+ default = Path(ts.name).stem + "_extracted.png"
633
+ self.editor.open_file_manager(
634
+ on_save=self._on_export_selected_path,
635
+ allowed_exts=[".png"],
636
+ mode="save",
637
+ default_name=default,
638
+ )
639
+
640
+ def _on_export_selected_path(self, path: Path):
641
+ try:
642
+ sx, sy, sw, sh = self.selected_tile
643
+ ts = self.tilesets[self.active_idx]
644
+ extracted = ts.surface.subsurface(Rect(sx, sy, sw, sh)).copy()
645
+ pygame.image.save(extracted, str(path))
646
+ except Exception as e:
647
+ error_handler.capture(e, context="export_selected_png")
648
+
634
649
  def draw(self, screen: pygame.Surface):
635
650
  self.draw_background(screen)
636
651
  self.draw_view_area(screen)
@@ -791,6 +806,15 @@ class TileSelector:
791
806
  screen.blit(name_surf, (self.rect.x + 5, self.rect.bottom - 30))
792
807
 
793
808
  def draw_buttons(self, screen):
809
+ # Export selection as PNG button
810
+ pygame.draw.rect(
811
+ screen, COLORS.header, self.btn_export, border_radius=SHAPE.radius_sm
812
+ )
813
+ screen.blit(
814
+ self.font.render("E", True, COLORS.text),
815
+ (self.btn_export.x + 9, self.btn_export.y + 5),
816
+ )
817
+
794
818
  # Collision editor button
795
819
  pygame.draw.rect(
796
820
  screen, COLORS.header, self.btn_collision, border_radius=SHAPE.radius_sm
@@ -815,7 +839,9 @@ class TileSelector:
815
839
  (self.btn_rem.x + 10, self.btn_rem.y + 5),
816
840
  )
817
841
  mx, my = pygame.mouse.get_pos()
818
- if self.btn_collision.collidepoint(mx, my):
842
+ if self.btn_export.collidepoint(mx, my):
843
+ self.editor.tooltip.show("E: Export selection as PNG", (mx + 10, my + 10))
844
+ elif self.btn_collision.collidepoint(mx, my):
819
845
  self.editor.tooltip.show("Edit Collision Shapes", (mx + 10, my + 10))
820
846
  elif self.btn_add.collidepoint(mx, my):
821
847
  self.editor.tooltip.show("Add Tileset", (mx + 10, my + 10))
@@ -0,0 +1,149 @@
1
+ import pygame
2
+ from pygame import Rect, Surface
3
+ from typing import Callable, Optional
4
+
5
+
6
+ class ConfirmDialog:
7
+ def __init__(self, editor_rect: Rect):
8
+ self.editor_rect = editor_rect
9
+ self.rect = Rect(0, 0, 420, 180)
10
+ self.rect.center = editor_rect.center
11
+
12
+ self.active = False
13
+ self.title = ""
14
+ self.message = ""
15
+ self.on_confirm: Optional[Callable[[], None]] = None
16
+ self.on_cancel: Optional[Callable[[], None]] = None
17
+
18
+ self.bg_color = (40, 40, 40)
19
+ self.border_color = (100, 100, 100)
20
+ self.text_color = (255, 255, 255)
21
+ self.warn_color = (255, 200, 60)
22
+ self.button_color = (60, 100, 180)
23
+ self.button_hover_color = (80, 120, 200)
24
+
25
+ self.btn_proceed = Rect(0, 0, 110, 30)
26
+ self.btn_cancel = Rect(0, 0, 110, 30)
27
+
28
+ self.font_title = pygame.font.SysFont("Arial", 18, bold=True)
29
+ self.font_text = pygame.font.SysFont("Arial", 14)
30
+ self.font_btn = pygame.font.SysFont("Arial", 14, bold=True)
31
+
32
+ self.btn_proceed_hover = False
33
+ self.btn_cancel_hover = False
34
+ self._layout()
35
+
36
+ def _layout(self):
37
+ self.rect.center = self.editor_rect.center
38
+ btn_y = self.rect.bottom - 50
39
+ cx = self.rect.centerx
40
+ self.btn_cancel = Rect(cx - 120, btn_y, 110, 30)
41
+ self.btn_proceed = Rect(cx + 10, btn_y, 110, 30)
42
+
43
+ def show(
44
+ self,
45
+ title: str,
46
+ message: str,
47
+ on_confirm: Callable[[], None],
48
+ on_cancel: Callable[[], None],
49
+ ):
50
+ if self.active:
51
+ self.hide()
52
+ self.active = True
53
+ self.title = title
54
+ self.message = message
55
+ self.on_confirm = on_confirm
56
+ self.on_cancel = on_cancel
57
+ self.btn_proceed_hover = False
58
+ self.btn_cancel_hover = False
59
+ self._layout()
60
+
61
+ def hide(self):
62
+ self.active = False
63
+
64
+ def handle_event(self, event: pygame.event.Event) -> bool:
65
+ if not self.active:
66
+ return False
67
+
68
+ self._layout()
69
+ mouse_pos = pygame.mouse.get_pos()
70
+
71
+ if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
72
+ if self.btn_proceed.collidepoint(mouse_pos):
73
+ if self.on_confirm:
74
+ self.on_confirm()
75
+ self.hide()
76
+ return True
77
+ if self.btn_cancel.collidepoint(mouse_pos):
78
+ if self.on_cancel:
79
+ self.on_cancel()
80
+ self.hide()
81
+ return True
82
+
83
+ elif event.type == pygame.KEYDOWN:
84
+ if event.key == pygame.K_ESCAPE:
85
+ if self.on_cancel:
86
+ self.on_cancel()
87
+ self.hide()
88
+ return True
89
+ elif event.key == pygame.K_RETURN:
90
+ if self.on_confirm:
91
+ self.on_confirm()
92
+ self.hide()
93
+ return True
94
+
95
+ elif event.type == pygame.MOUSEMOTION:
96
+ self.btn_proceed_hover = self.btn_proceed.collidepoint(mouse_pos)
97
+ self.btn_cancel_hover = self.btn_cancel.collidepoint(mouse_pos)
98
+
99
+ return False
100
+
101
+ def draw(self, surface: Surface):
102
+ if not self.active:
103
+ return
104
+
105
+ self._layout()
106
+ pygame.draw.rect(surface, self.bg_color, self.rect)
107
+ pygame.draw.rect(surface, self.border_color, self.rect, 2)
108
+
109
+ title_surf = self.font_title.render(self.title, True, self.warn_color)
110
+ title_rect = title_surf.get_rect(topleft=(self.rect.x + 20, self.rect.y + 15))
111
+ surface.blit(title_surf, title_rect)
112
+
113
+ lines = self._wrap_text(self.message, self.rect.width - 40)
114
+ y = self.rect.y + 50
115
+ for line in lines:
116
+ line_surf = self.font_text.render(line, True, self.text_color)
117
+ surface.blit(line_surf, (self.rect.x + 20, y))
118
+ y += 22
119
+
120
+ proceed_color = self.button_hover_color if self.btn_proceed_hover else self.button_color
121
+ cancel_color = self.button_hover_color if self.btn_cancel_hover else (60, 60, 60)
122
+
123
+ pygame.draw.rect(surface, cancel_color, self.btn_cancel)
124
+ pygame.draw.rect(surface, proceed_color, self.btn_proceed)
125
+
126
+ cancel_text = self.font_btn.render("Cancel", True, self.text_color)
127
+ proceed_text = self.font_btn.render("Proceed", True, self.text_color)
128
+
129
+ cancel_text_rect = cancel_text.get_rect(center=self.btn_cancel.center)
130
+ proceed_text_rect = proceed_text.get_rect(center=self.btn_proceed.center)
131
+
132
+ surface.blit(cancel_text, cancel_text_rect)
133
+ surface.blit(proceed_text, proceed_text_rect)
134
+
135
+ def _wrap_text(self, text: str, max_width: int):
136
+ words = text.split()
137
+ lines = []
138
+ current = ""
139
+ for word in words:
140
+ test = f"{current} {word}".strip()
141
+ if self.font_text.size(test)[0] <= max_width:
142
+ current = test
143
+ else:
144
+ if current:
145
+ lines.append(current)
146
+ current = word
147
+ if current:
148
+ lines.append(current)
149
+ return lines
File without changes
File without changes