tilemap-editor 3.2.7__tar.gz → 3.2.9__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 (199) hide show
  1. {tilemap_editor-3.2.7/src/tilemap_editor.egg-info → tilemap_editor-3.2.9}/PKG-INFO +1 -1
  2. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/pyproject.toml +1 -1
  3. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/editor.py +28 -5
  4. tilemap_editor-3.2.9/src/node_manager.py +147 -0
  5. tilemap_editor-3.2.9/src/nodes.py +53 -0
  6. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap.py +11 -0
  7. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/settings.py +1 -0
  8. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9/src/tilemap_editor.egg-info}/PKG-INFO +1 -1
  9. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor.egg-info/SOURCES.txt +4 -0
  10. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/ttypes/tilemap.py +1 -0
  11. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/layer_selector.py +63 -1
  12. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/tile_grid.py +244 -0
  13. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/tile_selector.py +21 -0
  14. tilemap_editor-3.2.9/src/widgets/ui/node_editor.py +337 -0
  15. tilemap_editor-3.2.9/src/widgets/ui/node_selector.py +602 -0
  16. tilemap_editor-3.2.9/src/widgets/ui/tileset_type_dialog.py +451 -0
  17. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/ui/toolbar.py +12 -1
  18. tilemap_editor-3.2.7/src/widgets/ui/tileset_type_dialog.py +0 -214
  19. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/LICENSE +0 -0
  20. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/MANIFEST.in +0 -0
  21. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/README.md +0 -0
  22. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
  23. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
  24. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
  25. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
  26. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
  27. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
  28. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
  29. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
  30. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/arrow-down.svg +0 -0
  31. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/check.svg +0 -0
  32. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/checked.svg +0 -0
  33. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/close.svg +0 -0
  34. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/duplicate.svg +0 -0
  35. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/error.svg +0 -0
  36. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/file.svg +0 -0
  37. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/filedead.svg +0 -0
  38. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/fit.svg +0 -0
  39. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/folder.svg +0 -0
  40. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/grid.svg +0 -0
  41. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/image.svg +0 -0
  42. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/info.svg +0 -0
  43. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/load.svg +0 -0
  44. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/loop.svg +0 -0
  45. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/pan.svg +0 -0
  46. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/pause.svg +0 -0
  47. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/pencil.svg +0 -0
  48. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/play.svg +0 -0
  49. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/plus.svg +0 -0
  50. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/radio.svg +0 -0
  51. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/reset.svg +0 -0
  52. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/save.svg +0 -0
  53. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/stop.svg +0 -0
  54. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/tilemap.svg +0 -0
  55. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/tileset.svg +0 -0
  56. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/unchecked.svg +0 -0
  57. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/warning.svg +0 -0
  58. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/zoomin.svg +0 -0
  59. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/assets/icons/zoomout.svg +0 -0
  60. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/setup.cfg +0 -0
  61. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/configs/themes.py +0 -0
  62. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/constants.py +0 -0
  63. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/event_map.py +0 -0
  64. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/layers.py +0 -0
  65. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/main.py +0 -0
  66. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/__init__.py +0 -0
  67. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/character_collision/__init__.py +0 -0
  68. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/character_collision/editor.py +0 -0
  69. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/character_collision/models.py +0 -0
  70. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/character_collision/protocols.py +0 -0
  71. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/character_collision/shape_editor.py +0 -0
  72. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/character_collision/standalone.py +0 -0
  73. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/object_tileset_collision/__init__.py +0 -0
  74. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/object_tileset_collision/editor.py +0 -0
  75. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/object_tileset_collision/models.py +0 -0
  76. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/object_tileset_collision/protocols.py +0 -0
  77. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/object_tileset_collision/standalone.py +0 -0
  78. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/sprite_animation/__init__.py +0 -0
  79. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/sprite_animation/__main__.py +0 -0
  80. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/sprite_animation/clipboard_util.py +0 -0
  81. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/sprite_animation/editor.py +0 -0
  82. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/sprite_animation/frame_picker.py +0 -0
  83. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/sprite_animation/models.py +0 -0
  84. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/sprite_animation/preview.py +0 -0
  85. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/sprite_animation/protocols.py +0 -0
  86. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/sprite_animation/runtime_load.py +0 -0
  87. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/sprite_animation/standalone.py +0 -0
  88. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/sprite_animation/timeline.py +0 -0
  89. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/sprite_animation/validation.py +0 -0
  90. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/sprite_editor/__init__.py +0 -0
  91. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/sprite_editor/editor.py +0 -0
  92. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/sprite_editor/standalone.py +0 -0
  93. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/tileset_collision/__init__.py +0 -0
  94. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/tileset_collision/collision_painter.py +0 -0
  95. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/tileset_collision/editor.py +0 -0
  96. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/tileset_collision/models.py +0 -0
  97. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/tileset_collision/protocols.py +0 -0
  98. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/plugins/tileset_collision/standalone.py +0 -0
  99. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/standalone_automap.py +0 -0
  100. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/standalone_error_console.py +0 -0
  101. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/standalone_filemanager.py +0 -0
  102. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/standalone_image_viewer.py +0 -0
  103. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/__init__.py +0 -0
  104. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/__main__.py +0 -0
  105. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/__init__.py +0 -0
  106. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
  107. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
  108. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
  109. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
  110. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
  111. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
  112. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
  113. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
  114. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/ToolMove.svg +0 -0
  115. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/arrow-down.svg +0 -0
  116. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/check.svg +0 -0
  117. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/checked.svg +0 -0
  118. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/close.svg +0 -0
  119. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/duplicate.svg +0 -0
  120. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/error.svg +0 -0
  121. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/file.svg +0 -0
  122. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/filedead.svg +0 -0
  123. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/fit.svg +0 -0
  124. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/folder.svg +0 -0
  125. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/grid.svg +0 -0
  126. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/image.svg +0 -0
  127. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/info.svg +0 -0
  128. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/load.svg +0 -0
  129. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/loop.svg +0 -0
  130. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/pan.svg +0 -0
  131. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/pause.svg +0 -0
  132. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/pencil.svg +0 -0
  133. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/play.svg +0 -0
  134. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/plus.svg +0 -0
  135. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/radio.svg +0 -0
  136. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/reset.svg +0 -0
  137. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/save.svg +0 -0
  138. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/stop.svg +0 -0
  139. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/tilemap.svg +0 -0
  140. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/tileset.svg +0 -0
  141. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/unchecked.svg +0 -0
  142. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/warning.svg +0 -0
  143. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/zoomin.svg +0 -0
  144. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/assets/icons/zoomout.svg +0 -0
  145. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/cli.py +0 -0
  146. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor/main.py +0 -0
  147. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor.egg-info/dependency_links.txt +0 -0
  148. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor.egg-info/entry_points.txt +0 -0
  149. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor.egg-info/requires.txt +0 -0
  150. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/tilemap_editor.egg-info/top_level.txt +0 -0
  151. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/ttypes/__init__.py +0 -0
  152. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/utils/__init__.py +0 -0
  153. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/utils/editor_preference.py +0 -0
  154. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/utils/error_handler.py +0 -0
  155. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/utils/font_manager.py +0 -0
  156. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/utils/history.py +0 -0
  157. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/utils/icon_manager.py +0 -0
  158. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/utils/icons_cache.py +0 -0
  159. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/utils/log_capture.py +0 -0
  160. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/utils/project_paths.py +0 -0
  161. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/utils/serialization.py +0 -0
  162. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/utils/standalone.py +0 -0
  163. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/utils/validation.py +0 -0
  164. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/__init__.py +0 -0
  165. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/automap_models.py +0 -0
  166. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/autotile_template.py +0 -0
  167. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/autotiler.py +0 -0
  168. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/filemanager.py +0 -0
  169. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/input.py +0 -0
  170. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/map_properties.py +0 -0
  171. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/mapsetup.py +0 -0
  172. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/regex_automap_designer.py +0 -0
  173. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/spritesheet_grid.py +0 -0
  174. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/ui/collision_layer_mask.py +0 -0
  175. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/ui/collision_layer_sidebar.py +0 -0
  176. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/ui/confirm_dialog.py +0 -0
  177. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/ui/draw_utils.py +0 -0
  178. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/ui/fileinput.py +0 -0
  179. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/ui/layer_type_dialog.py +0 -0
  180. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/ui/menubar.py +0 -0
  181. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/ui/mode_indicator.py +0 -0
  182. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/ui/notification.py +0 -0
  183. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/ui/property_editor.py +0 -0
  184. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/ui/region_selector.py +0 -0
  185. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/ui/status_bar.py +0 -0
  186. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/ui/theme.py +0 -0
  187. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/src/widgets/ui/tooltip.py +0 -0
  188. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/tests/test_animation_library_grid_offset.py +0 -0
  189. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/tests/test_collision_layer_mask.py +0 -0
  190. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/tests/test_collision_layer_sidebar.py +0 -0
  191. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/tests/test_editor_pan_mode.py +0 -0
  192. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/tests/test_project_paths.py +0 -0
  193. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/tests/test_render_scale.py +0 -0
  194. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/tests/test_sprite_animation_editor_grid.py +0 -0
  195. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/tests/test_tile_grid_selection.py +0 -0
  196. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/tests/test_tile_selector_pick.py +0 -0
  197. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/tests/test_tilemap_save.py +0 -0
  198. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/tests/test_timeline_invalidate_cache.py +0 -0
  199. {tilemap_editor-3.2.7 → tilemap_editor-3.2.9}/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.7
3
+ Version: 3.2.9
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.7"
7
+ version = "3.2.9"
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"
@@ -31,6 +31,8 @@ from widgets.ui.confirm_dialog import ConfirmDialog
31
31
  from widgets.ui.layer_type_dialog import LayerTypeDialog
32
32
  from widgets.ui.menubar import MenuBar
33
33
  from widgets.ui.toolbar import Toolbar
34
+ from widgets.ui.node_selector import NodeSelector
35
+ from widgets.ui.node_editor import NodeEditor
34
36
  from widgets.ui.notification import NotificationManager
35
37
  from widgets.ui.property_editor import PropertyEditor
36
38
  from widgets.ui.tooltip import TooltipManager
@@ -38,6 +40,7 @@ from widgets.ui.theme import get_theme_manager, set_theme, THEMES
38
40
  from utils.log_capture import setup_console_log
39
41
  from utils.standalone import launch_standalone
40
42
  from utils import error_handler, error_context
43
+ from node_manager import NodeManager
41
44
 
42
45
  if TYPE_CHECKING:
43
46
  from plugins.sprite_animation import SpriteAnimationEditor
@@ -153,6 +156,7 @@ class Editor:
153
156
  self.autotile_mode = False
154
157
  self.eraser_mode = False
155
158
  self.select_mode = False
159
+ self.node_editing_mode = False
156
160
  self._prev_tool = None
157
161
 
158
162
  if isinstance(size, tuple) and len(size) == 2:
@@ -240,6 +244,9 @@ class Editor:
240
244
  self.map_setup_widget = MapSetup(self, Rect(center_x, center_y, 400, 400))
241
245
  self.map_setup_widget.visible = False
242
246
  self.map_properties_dialog = MapPropertiesDialog(self, Rect(center_x, center_y, 400, 260))
247
+ self.node_manager = NodeManager(self)
248
+ self.node_selector = NodeSelector(self, 0, 65, 260, 240)
249
+ self.node_editor = NodeEditor(self, 0, 310, 260, 190)
243
250
 
244
251
  def open_file_manager(
245
252
  self,
@@ -1126,7 +1133,7 @@ class Editor:
1126
1133
  elif event.key == pygame.K_o and (ctrl_held or meta_held):
1127
1134
  self.perform_load()
1128
1135
  continue
1129
- elif event.key == pygame.K_SPACE:
1136
+ elif event.key == pygame.K_SPACE and (ctrl_held or meta_held):
1130
1137
  if self.pan_mode:
1131
1138
  # Restoring: turn off pan, re-enable previous tool
1132
1139
  self.pan_mode = False
@@ -1134,17 +1141,22 @@ class Editor:
1134
1141
  self.select_mode = True
1135
1142
  elif getattr(self, "_prev_tool", None) == "eraser":
1136
1143
  self.eraser_mode = True
1144
+ elif getattr(self, "_prev_tool", None) == "nodes":
1145
+ self.node_editing_mode = True
1137
1146
  else:
1138
1147
  # Entering pan: save current tool, turn off others
1139
1148
  if self.select_mode:
1140
1149
  self._prev_tool = "select"
1141
1150
  elif self.eraser_mode:
1142
1151
  self._prev_tool = "eraser"
1152
+ elif self.node_editing_mode:
1153
+ self._prev_tool = "nodes"
1143
1154
  else:
1144
1155
  self._prev_tool = None
1145
1156
  self.pan_mode = True
1146
1157
  self.select_mode = False
1147
1158
  self.eraser_mode = False
1159
+ self.node_editing_mode = False
1148
1160
  continue
1149
1161
  elif event.mod & pygame.KMOD_CTRL and event.key == pygame.K_g:
1150
1162
  self.toggle_grid()
@@ -1156,15 +1168,22 @@ class Editor:
1156
1168
  self.toggle_animation_panel()
1157
1169
  continue
1158
1170
  elif pygame.K_1 <= event.key <= pygame.K_9:
1159
- idx = event.key - pygame.K_1
1160
- if idx < self.tilemap.layer_manager.get_layer_count():
1161
- self.tilemap.layer_manager.set_active_layer(idx)
1162
- continue
1171
+ if not (self.node_editor and self.node_editor.visible and self.node_editor.editing_field):
1172
+ idx = event.key - pygame.K_1
1173
+ if idx < self.tilemap.layer_manager.get_layer_count():
1174
+ self.tilemap.layer_manager.set_active_layer(idx)
1175
+ continue
1163
1176
 
1164
1177
  # Priority 5: Toolbar
1165
1178
  if self.toolbar and self.toolbar.handle_event(event):
1166
1179
  continue
1167
1180
 
1181
+ # Priority 5.5: Node selector & editor (when node mode active)
1182
+ if self.node_selector and self.node_selector.handle_event(event):
1183
+ continue
1184
+ if self.node_editor and self.node_editor.handle_event(event):
1185
+ continue
1186
+
1168
1187
  # Priority 6: Dockable animation panel
1169
1188
  if self.left_panel_visible and self.animation_panel:
1170
1189
  if hasattr(self.animation_panel, "handle_event"):
@@ -1303,6 +1322,10 @@ class Editor:
1303
1322
 
1304
1323
  if self.toolbar:
1305
1324
  self.toolbar.draw(self.screen)
1325
+ if self.node_selector:
1326
+ self.node_selector.draw(self.screen)
1327
+ if self.node_editor:
1328
+ self.node_editor.draw(self.screen)
1306
1329
  self.menubar.draw(self.screen)
1307
1330
  self.tooltip.draw(self.screen)
1308
1331
 
@@ -0,0 +1,147 @@
1
+ import json
2
+ import uuid
3
+ from pathlib import Path
4
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple
5
+
6
+ from nodes import Node, NodeRect
7
+
8
+ if TYPE_CHECKING:
9
+ from editor import Editor
10
+
11
+
12
+ def create_node_id() -> str:
13
+ return str(uuid.uuid4())
14
+
15
+
16
+ SIDECAR_VERSION = 1
17
+
18
+
19
+ class NodeManager:
20
+ def __init__(self, editor: "Editor") -> None:
21
+ self.editor = editor
22
+ self.nodes: Dict[str, Node] = {}
23
+ self.active_node_id: Optional[str] = None
24
+ self.active_group_name: Optional[str] = None
25
+ self.groups: List[str] = []
26
+ self.default_node_type: str = "area"
27
+ self._nodes_dir: Optional[Path] = None
28
+
29
+ @property
30
+ def nodes_dir(self) -> Path:
31
+ if self._nodes_dir is None:
32
+ config = getattr(self.editor, "config", {})
33
+ rel = config.get("nodes_path", "nodes")
34
+ self._nodes_dir = self.editor.data_root / rel
35
+ return self._nodes_dir
36
+
37
+ def _sidecar_path_for(self, map_path: Path) -> Path:
38
+ return self.nodes_dir / f"{map_path.stem}.nodes.json"
39
+
40
+ def load(self, map_path: Path) -> None:
41
+ self.nodes.clear()
42
+ self.groups.clear()
43
+ self.active_node_id = None
44
+ self.active_group_name = None
45
+ sidecar = self._sidecar_path_for(map_path)
46
+ if not sidecar.is_file():
47
+ return
48
+ try:
49
+ raw = json.loads(sidecar.read_text(encoding="utf-8"))
50
+ if not isinstance(raw, dict):
51
+ return
52
+ self.groups = list(raw.get("groups", []))
53
+ for item in raw.get("nodes", []):
54
+ if isinstance(item, dict):
55
+ node = Node.from_dict(item)
56
+ self.nodes[node.node_id] = node
57
+ except (json.JSONDecodeError, KeyError, ValueError, TypeError):
58
+ import logging
59
+ logging.warning(f"Failed to load nodes from {sidecar}")
60
+
61
+ def save(self, map_path: Path) -> None:
62
+ sidecar = self._sidecar_path_for(map_path)
63
+ self.nodes_dir.mkdir(parents=True, exist_ok=True)
64
+ data: Dict[str, Any] = {
65
+ "version": SIDECAR_VERSION,
66
+ "groups": self.groups,
67
+ "nodes": [node.to_dict() for node in self.nodes.values()],
68
+ }
69
+ sidecar.write_text(json.dumps(data, indent=2), encoding="utf-8")
70
+
71
+ def add_node(self, node: Node) -> str:
72
+ self.nodes[node.node_id] = node
73
+ return node.node_id
74
+
75
+ def remove_node(self, node_id: str) -> None:
76
+ self.nodes.pop(node_id, None)
77
+ if self.active_node_id == node_id:
78
+ self.active_node_id = None
79
+
80
+ def get_node(self, node_id: str) -> Optional[Node]:
81
+ return self.nodes.get(node_id)
82
+
83
+ def get_active_node(self) -> Optional[Node]:
84
+ if self.active_node_id is None:
85
+ return None
86
+ return self.nodes.get(self.active_node_id)
87
+
88
+ def set_active_node(self, node_id: Optional[str]) -> None:
89
+ self.active_node_id = node_id
90
+ if node_id is not None:
91
+ self.active_group_name = None
92
+
93
+ def set_active_group(self, group_name: Optional[str]) -> None:
94
+ self.active_group_name = group_name
95
+ if group_name is not None:
96
+ self.active_node_id = None
97
+
98
+ def get_nodes_for_layer(self, layer_name: str) -> List[Node]:
99
+ return [n for n in self.nodes.values() if n.layer_name == layer_name]
100
+
101
+ def create_default_node(self, layer_name: str, node_type: str = "area") -> Node:
102
+ count = len(self.nodes) + 1
103
+ return Node(
104
+ node_id=create_node_id(),
105
+ name=f"{node_type.capitalize()} {count}",
106
+ node_type=node_type,
107
+ area=NodeRect(x=0, y=0, w=64, h=64),
108
+ layer_name=layer_name,
109
+ )
110
+
111
+ def rename_group(self, old_name: str, new_name: str) -> bool:
112
+ if not new_name or new_name == old_name:
113
+ return False
114
+ if new_name in self.groups:
115
+ return False # Already exists
116
+ if old_name not in self.groups:
117
+ return False
118
+
119
+ # Update groups list
120
+ idx = self.groups.index(old_name)
121
+ self.groups[idx] = new_name
122
+
123
+ # Update child nodes
124
+ for node in self.nodes.values():
125
+ if node.group == old_name:
126
+ node.group = new_name
127
+
128
+ if self.active_group_name == old_name:
129
+ self.active_group_name = new_name
130
+ return True
131
+
132
+ def reorder_node(self, node_id: str, target_node_id: str, before: bool = True) -> None:
133
+ if node_id not in self.nodes or target_node_id not in self.nodes:
134
+ return
135
+ node = self.nodes[node_id]
136
+ keys = list(self.nodes.keys())
137
+ keys.remove(node_id)
138
+ target_idx = keys.index(target_node_id)
139
+ if not before:
140
+ target_idx += 1
141
+ keys.insert(target_idx, node_id)
142
+
143
+ # Rebuild dictionary
144
+ new_nodes = {}
145
+ for k in keys:
146
+ new_nodes[k] = self.nodes[k]
147
+ self.nodes = new_nodes
@@ -0,0 +1,53 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Any, Dict, Optional
5
+
6
+
7
+ @dataclass
8
+ class NodeRect:
9
+ x: int
10
+ y: int
11
+ w: int
12
+ h: int
13
+
14
+ def to_dict(self) -> Dict[str, int]:
15
+ return {"x": self.x, "y": self.y, "w": self.w, "h": self.h}
16
+
17
+ @classmethod
18
+ def from_dict(cls, data: Dict[str, Any]) -> "NodeRect":
19
+ return cls(x=int(data["x"]), y=int(data["y"]), w=int(data["w"]), h=int(data["h"]))
20
+
21
+
22
+ @dataclass
23
+ class Node:
24
+ node_id: str
25
+ name: str
26
+ node_type: str
27
+ area: NodeRect
28
+ layer_name: str
29
+ properties: Dict[str, Any] = field(default_factory=dict)
30
+ group: Optional[str] = None
31
+
32
+ def to_dict(self) -> Dict[str, Any]:
33
+ return {
34
+ "node_id": self.node_id,
35
+ "name": self.name,
36
+ "node_type": self.node_type,
37
+ "area": self.area.to_dict(),
38
+ "layer_name": self.layer_name,
39
+ "properties": dict(self.properties),
40
+ "group": self.group,
41
+ }
42
+
43
+ @classmethod
44
+ def from_dict(cls, data: Dict[str, Any]) -> "Node":
45
+ return cls(
46
+ node_id=str(data["node_id"]),
47
+ name=str(data["name"]),
48
+ node_type=str(data.get("node_type", "area")),
49
+ area=NodeRect.from_dict(data["area"]),
50
+ layer_name=str(data.get("layer_name", "")),
51
+ properties=dict(data.get("properties", {})),
52
+ group=data.get("group"),
53
+ )
@@ -298,6 +298,8 @@ class Tilemap:
298
298
  ts_data["tile_properties"] = {
299
299
  str(k): v for k, v in ts.tile_properties.items()
300
300
  }
301
+ if ts.animation:
302
+ ts_data["animation"] = ts.animation
301
303
 
302
304
  save_data["resources"]["tilesets"].append(ts_data)
303
305
 
@@ -394,6 +396,9 @@ class Tilemap:
394
396
  with open(target_path, "w") as f:
395
397
  JSONDump(save_data, f, indent=2)
396
398
 
399
+ if hasattr(self.editor, "node_manager"):
400
+ self.editor.node_manager.save(target_path)
401
+
397
402
  print(f"Saved to {target_path}")
398
403
 
399
404
  def _project_base_path(self) -> Path:
@@ -544,6 +549,7 @@ class Tilemap:
544
549
  tileset_type,
545
550
  properties=ts_entry.get("properties", {}),
546
551
  tile_properties=ts_entry.get("tile_properties", {}),
552
+ animation=ts_entry.get("animation"),
547
553
  )
548
554
  except Exception as e:
549
555
  error_msg = f"Error loading tileset {path_str}: {e}"
@@ -654,6 +660,11 @@ class Tilemap:
654
660
  if active_layer:
655
661
  active_layer.tiles[pos] = tile_data
656
662
 
663
+ if hasattr(self.editor, "node_manager"):
664
+ self.editor.node_manager.load(path)
665
+ if hasattr(self.editor, "node_selector"):
666
+ self.editor.node_selector._rebuild_filter()
667
+
657
668
  self.initialized = True
658
669
 
659
670
  def _object_tileset_indices_from_payload(self, payload: dict) -> set[int]:
@@ -23,6 +23,7 @@ def init_settings(generate_main: bool = False) -> None:
23
23
  "tileset": "collision",
24
24
  "character": "character_collision"
25
25
  },
26
+ "nodes_path": "nodes",
26
27
  "error_handler": {
27
28
  "log_path": "errors.log",
28
29
  "max_recent_errors": 50,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tilemap-editor
3
- Version: 3.2.7
3
+ Version: 3.2.9
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
@@ -45,6 +45,8 @@ src/editor.py
45
45
  src/event_map.py
46
46
  src/layers.py
47
47
  src/main.py
48
+ src/node_manager.py
49
+ src/nodes.py
48
50
  src/standalone_automap.py
49
51
  src/standalone_error_console.py
50
52
  src/standalone_filemanager.py
@@ -170,6 +172,8 @@ src/widgets/ui/fileinput.py
170
172
  src/widgets/ui/layer_type_dialog.py
171
173
  src/widgets/ui/menubar.py
172
174
  src/widgets/ui/mode_indicator.py
175
+ src/widgets/ui/node_editor.py
176
+ src/widgets/ui/node_selector.py
173
177
  src/widgets/ui/notification.py
174
178
  src/widgets/ui/property_editor.py
175
179
  src/widgets/ui/region_selector.py
@@ -64,6 +64,7 @@ class TypeTilesetSerialized(TypedDict, total=True):
64
64
  type: str
65
65
  properties: NotRequired[Dict[str, Any]]
66
66
  tile_properties: NotRequired[Dict[str, Dict[str, Any]]] # variant_id string -> properties
67
+ animation: NotRequired[dict]
67
68
 
68
69
 
69
70
  TTile = Dict[Tuple[int, int], TypeTile]
@@ -44,6 +44,8 @@ class LayerSelector:
44
44
  self.rename_text: str = ""
45
45
  self.rename_original_name: str = ""
46
46
 
47
+ self._adjusting_opacity_idx: Optional[int] = None
48
+
47
49
  btn_h = 25
48
50
  btn_w = 25
49
51
  btn_y = self.footer_rect.y + 5
@@ -113,6 +115,19 @@ class LayerSelector:
113
115
  layer.locked = not layer.locked
114
116
  return True
115
117
 
118
+ opacity_rect = self._get_opacity_bar_rect(layer_idx)
119
+ if opacity_rect and opacity_rect.collidepoint(mouse_pos):
120
+ layer = self.editor.tilemap.layer_manager.get_layer(
121
+ layer_idx
122
+ )
123
+ if layer:
124
+ self._adjusting_opacity_idx = layer_idx
125
+ rel_x = mouse_pos[0] - opacity_rect.x
126
+ layer.opacity = max(
127
+ 0.0, min(1.0, rel_x / opacity_rect.width)
128
+ )
129
+ return True
130
+
116
131
  self.dragging_layer_idx = layer_idx
117
132
  self.drag_start_y = mouse_pos[1]
118
133
 
@@ -153,6 +168,9 @@ class LayerSelector:
153
168
  return True
154
169
 
155
170
  elif event.type == pygame.MOUSEBUTTONUP:
171
+ if event.button == 1:
172
+ self._adjusting_opacity_idx = None
173
+
156
174
  if event.button == 1 and self.dragging_layer_idx is not None:
157
175
  layer_idx = self._get_layer_at_pos(mouse_pos)
158
176
  if layer_idx is not None and layer_idx != self.dragging_layer_idx:
@@ -164,6 +182,19 @@ class LayerSelector:
164
182
  return True
165
183
 
166
184
  elif event.type == pygame.MOUSEMOTION:
185
+ if self._adjusting_opacity_idx is not None:
186
+ layer = self.editor.tilemap.layer_manager.get_layer(
187
+ self._adjusting_opacity_idx
188
+ )
189
+ if layer:
190
+ opacity_rect = self._get_opacity_bar_rect(
191
+ self._adjusting_opacity_idx
192
+ )
193
+ if opacity_rect:
194
+ rel_x = max(0, min(opacity_rect.width, mouse_pos[0] - opacity_rect.x))
195
+ layer.opacity = max(0.0, min(1.0, rel_x / opacity_rect.width))
196
+ return True
197
+
167
198
  if self.list_rect.collidepoint(mouse_pos):
168
199
  if self.dragging_layer_idx is None:
169
200
  self.hover_idx = self._get_layer_at_pos(mouse_pos)
@@ -188,7 +219,13 @@ class LayerSelector:
188
219
  self._cancel_rename()
189
220
  return True
190
221
  elif event.key == pygame.K_BACKSPACE:
191
- self.rename_text = self.rename_text[:-1]
222
+ pressed = pygame.key.get_pressed()
223
+ meta_down = pressed[pygame.K_LMETA] or pressed[pygame.K_RMETA]
224
+ ctrl_down = pressed[pygame.K_LCTRL] or pressed[pygame.K_RCTRL]
225
+ if meta_down or ctrl_down:
226
+ self.rename_text = ""
227
+ else:
228
+ self.rename_text = self.rename_text[:-1]
192
229
  return True
193
230
  else:
194
231
  if event.unicode.isprintable():
@@ -219,6 +256,19 @@ class LayerSelector:
219
256
 
220
257
  return None
221
258
 
259
+ def _get_opacity_bar_rect(self, layer_idx: int) -> Optional[Rect]:
260
+ """Get the clickable rect for the opacity bar of a layer."""
261
+ item_y = self.list_rect.y + (layer_idx * self.item_h) - self.scroll_offset
262
+
263
+ if item_y + self.item_h < self.list_rect.y or item_y > self.list_rect.bottom:
264
+ return None
265
+
266
+ bar_w = 46
267
+ bar_h = 6
268
+ bar_x = self.list_rect.x + self.list_rect.width - 80
269
+ bar_y = item_y + self.item_h - bar_h - 3
270
+ return Rect(bar_x, bar_y, bar_w, bar_h)
271
+
222
272
  def _get_eye_icon_rect(self, layer_idx: int, mouse_pos) -> Optional[Rect]:
223
273
  """Get the clickable rect for the eye icon of a layer."""
224
274
  if layer_idx is None:
@@ -404,6 +454,18 @@ class LayerSelector:
404
454
  name_txt = self.font_layer.render(layer.name, True, self.text_color)
405
455
  screen.blit(name_txt, (item_rect.x + 5, item_rect.y + 5))
406
456
 
457
+ opacity_bar = self._get_opacity_bar_rect(i)
458
+ if opacity_bar:
459
+ bg_rect = Rect(opacity_bar.x, opacity_bar.y, opacity_bar.width, opacity_bar.height)
460
+ pygame.draw.rect(screen, (60, 60, 60), bg_rect, border_radius=2)
461
+ fill_w = int(opacity_bar.width * layer.opacity)
462
+ if fill_w > 0:
463
+ fill_rect = Rect(opacity_bar.x, opacity_bar.y, fill_w, opacity_bar.height)
464
+ green = int(180 * layer.opacity) + 40
465
+ pygame.draw.rect(screen, (40, green, 40), fill_rect, border_radius=2)
466
+ pct_txt = self.font_layer.render(f"{int(layer.opacity * 100)}%", True, self.text_muted)
467
+ screen.blit(pct_txt, (opacity_bar.x - 32, opacity_bar.y - 2))
468
+
407
469
  eye_x = item_rect.right - 25
408
470
  eye_y = item_rect.y + 7
409
471
  if layer.visible: