tilemap-editor 2.0.2__tar.gz → 2.0.3__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 (123) hide show
  1. {tilemap_editor-2.0.2/src/tilemap_editor.egg-info → tilemap_editor-2.0.3}/PKG-INFO +1 -1
  2. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/pyproject.toml +1 -1
  3. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/editor.py +30 -9
  4. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3/src/tilemap_editor.egg-info}/PKG-INFO +1 -1
  5. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/widgets/autotile_template.py +24 -23
  6. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/widgets/autotiler.py +64 -39
  7. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/CHANGELOG.md +0 -0
  8. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/LICENSE +0 -0
  9. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/MANIFEST.in +0 -0
  10. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/README.md +0 -0
  11. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
  12. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
  13. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
  14. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
  15. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
  16. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
  17. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
  18. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
  19. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/icons/arrow-down.svg +0 -0
  20. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/icons/check.svg +0 -0
  21. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/icons/checked.svg +0 -0
  22. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/icons/close.svg +0 -0
  23. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/icons/duplicate.svg +0 -0
  24. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/icons/error.svg +0 -0
  25. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/icons/file.svg +0 -0
  26. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/icons/filedead.svg +0 -0
  27. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/icons/fit.svg +0 -0
  28. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/icons/folder.svg +0 -0
  29. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/icons/grid.svg +0 -0
  30. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/icons/image.svg +0 -0
  31. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/icons/info.svg +0 -0
  32. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/icons/load.svg +0 -0
  33. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/icons/loop.svg +0 -0
  34. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/icons/pan.svg +0 -0
  35. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/icons/pause.svg +0 -0
  36. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/icons/pencil.svg +0 -0
  37. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/icons/play.svg +0 -0
  38. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/icons/plus.svg +0 -0
  39. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/icons/radio.svg +0 -0
  40. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/icons/reset.svg +0 -0
  41. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/icons/save.svg +0 -0
  42. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/icons/stop.svg +0 -0
  43. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/icons/tilemap.svg +0 -0
  44. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/icons/tileset.svg +0 -0
  45. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/icons/unchecked.svg +0 -0
  46. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/icons/warning.svg +0 -0
  47. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/icons/zoomin.svg +0 -0
  48. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/assets/icons/zoomout.svg +0 -0
  49. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/setup.cfg +0 -0
  50. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/configs/themes.py +0 -0
  51. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/constants.py +0 -0
  52. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/event_map.py +0 -0
  53. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/layers.py +0 -0
  54. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/main.py +0 -0
  55. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/plugins/__init__.py +0 -0
  56. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/plugins/character_collision/__init__.py +0 -0
  57. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/plugins/character_collision/editor.py +0 -0
  58. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/plugins/character_collision/models.py +0 -0
  59. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/plugins/character_collision/protocols.py +0 -0
  60. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/plugins/character_collision/shape_editor.py +0 -0
  61. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/plugins/character_collision/standalone.py +0 -0
  62. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/plugins/sprite_animation/__init__.py +0 -0
  63. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/plugins/sprite_animation/__main__.py +0 -0
  64. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/plugins/sprite_animation/clipboard_util.py +0 -0
  65. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/plugins/sprite_animation/editor.py +0 -0
  66. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/plugins/sprite_animation/frame_picker.py +0 -0
  67. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/plugins/sprite_animation/models.py +0 -0
  68. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/plugins/sprite_animation/preview.py +0 -0
  69. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/plugins/sprite_animation/protocols.py +0 -0
  70. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/plugins/sprite_animation/runtime_load.py +0 -0
  71. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/plugins/sprite_animation/standalone.py +0 -0
  72. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/plugins/sprite_animation/timeline.py +0 -0
  73. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/plugins/sprite_animation/validation.py +0 -0
  74. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/plugins/tileset_collision/__init__.py +0 -0
  75. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/plugins/tileset_collision/collision_painter.py +0 -0
  76. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/plugins/tileset_collision/editor.py +0 -0
  77. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/plugins/tileset_collision/models.py +0 -0
  78. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/plugins/tileset_collision/protocols.py +0 -0
  79. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/plugins/tileset_collision/standalone.py +0 -0
  80. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/standalone_automap.py +0 -0
  81. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/standalone_error_console.py +0 -0
  82. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/standalone_filemanager.py +0 -0
  83. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/standalone_image_viewer.py +0 -0
  84. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/tilemap.py +0 -0
  85. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/tilemap_editor/__init__.py +0 -0
  86. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/tilemap_editor/__main__.py +0 -0
  87. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/tilemap_editor/cli.py +0 -0
  88. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/tilemap_editor.egg-info/SOURCES.txt +0 -0
  89. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/tilemap_editor.egg-info/dependency_links.txt +0 -0
  90. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/tilemap_editor.egg-info/entry_points.txt +0 -0
  91. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/tilemap_editor.egg-info/requires.txt +0 -0
  92. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/tilemap_editor.egg-info/top_level.txt +0 -0
  93. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/ttypes/__init__.py +0 -0
  94. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/ttypes/tilemap.py +0 -0
  95. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/utils/__init__.py +0 -0
  96. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/utils/error_handler.py +0 -0
  97. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/utils/font_manager.py +0 -0
  98. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/utils/history.py +0 -0
  99. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/utils/icon_manager.py +0 -0
  100. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/utils/icons_cache.py +0 -0
  101. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/utils/log_capture.py +0 -0
  102. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/utils/serialization.py +0 -0
  103. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/utils/settings.py +0 -0
  104. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/utils/standalone.py +0 -0
  105. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/utils/validation.py +0 -0
  106. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/widgets/__init__.py +0 -0
  107. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/widgets/automap_models.py +0 -0
  108. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/widgets/filemanager.py +0 -0
  109. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/widgets/layer_selector.py +0 -0
  110. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/widgets/mapsetup.py +0 -0
  111. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/widgets/regex_automap_designer.py +0 -0
  112. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/widgets/tile_grid.py +0 -0
  113. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/widgets/tile_selector.py +0 -0
  114. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/widgets/ui/draw_utils.py +0 -0
  115. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/widgets/ui/fileinput.py +0 -0
  116. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/widgets/ui/layer_type_dialog.py +0 -0
  117. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/widgets/ui/menubar.py +0 -0
  118. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/widgets/ui/notification.py +0 -0
  119. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/widgets/ui/property_editor.py +0 -0
  120. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/widgets/ui/theme.py +0 -0
  121. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/widgets/ui/tileset_type_dialog.py +0 -0
  122. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/widgets/ui/toolbar.py +0 -0
  123. {tilemap_editor-2.0.2 → tilemap_editor-2.0.3}/src/widgets/ui/tooltip.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tilemap-editor
3
- Version: 2.0.2
3
+ Version: 2.0.3
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 = "2.0.2"
7
+ version = "2.0.3"
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"
@@ -762,6 +762,7 @@ class Editor:
762
762
  if event.type == pygame.VIDEORESIZE:
763
763
  self.handle_resize(event.w, event.h)
764
764
 
765
+ # Priority 1: Modal dialogs and inputs (highest priority)
765
766
  if self.save_input.active:
766
767
  self.save_input.handle_event(event)
767
768
  continue
@@ -782,9 +783,20 @@ class Editor:
782
783
  if self.property_editor.handle_event(event):
783
784
  continue
784
785
 
786
+ # Priority 2: Autotiler and Regex Designer (block all events when visible)
787
+ if self.autotiler.visible:
788
+ if self.autotiler.handle_event(event):
789
+ continue
790
+
791
+ if self.regex_automap_designer.visible:
792
+ if self.regex_automap_designer.handle_event(event):
793
+ continue
794
+
795
+ # Priority 3: Menu bar
785
796
  if self.menubar.handle_event(event):
786
797
  continue
787
798
 
799
+ # Priority 4: Keyboard shortcuts
788
800
  if event.type == pygame.KEYDOWN:
789
801
  mods = pygame.key.get_mods()
790
802
  ctrl_held = mods & (pygame.KMOD_LCTRL | pygame.KMOD_RCTRL)
@@ -836,23 +848,17 @@ class Editor:
836
848
  self.tilemap.layer_manager.set_active_layer(idx)
837
849
  continue
838
850
 
851
+ # Priority 5: Toolbar
839
852
  if self.toolbar and self.toolbar.handle_event(event):
840
853
  continue
841
854
 
842
- if self.autotiler.visible:
843
- if self.autotiler.handle_event(event):
844
- continue
845
-
846
- if self.regex_automap_designer.visible:
847
- if self.regex_automap_designer.handle_event(event):
848
- continue
849
-
850
- # Route events to the dockable animation panel
855
+ # Priority 6: Dockable animation panel
851
856
  if self.left_panel_visible and self.animation_panel:
852
857
  if hasattr(self.animation_panel, "handle_event"):
853
858
  if self.animation_panel.handle_event(event):
854
859
  continue
855
860
 
861
+ # Priority 7: Side panels (tileset and layer widgets)
856
862
  consumed = False
857
863
  if self.tileset_widget and self.tileset_widget.handle_event(event):
858
864
  consumed = True
@@ -862,6 +868,8 @@ class Editor:
862
868
  and self.layer_widget.handle_event(event)
863
869
  ):
864
870
  consumed = True
871
+
872
+ # Priority 8: Main tile grid (lowest priority)
865
873
  if not consumed and self.tile_grid_widget:
866
874
  self.tile_grid_widget.handle_event(event)
867
875
 
@@ -894,9 +902,22 @@ class Editor:
894
902
  self.tileset_widget.draw(self.screen)
895
903
  if self.layer_widget:
896
904
  self.layer_widget.draw(self.screen)
905
+
906
+ # Draw autotiler and regex designer with modal overlay
897
907
  if self.autotiler:
908
+ if self.autotiler.visible:
909
+ # Dim background when autotiler is open
910
+ overlay = pygame.Surface((self.width, self.height), pygame.SRCALPHA)
911
+ overlay.fill((0, 0, 0, 100))
912
+ self.screen.blit(overlay, (0, 0))
898
913
  self.autotiler.draw(self.screen)
914
+
899
915
  if self.regex_automap_designer:
916
+ if self.regex_automap_designer.visible:
917
+ # Dim background when regex designer is open
918
+ overlay = pygame.Surface((self.width, self.height), pygame.SRCALPHA)
919
+ overlay.fill((0, 0, 0, 100))
920
+ self.screen.blit(overlay, (0, 0))
900
921
  self.regex_automap_designer.draw(self.screen)
901
922
 
902
923
  # Draw the dockable animation panel
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tilemap-editor
3
- Version: 2.0.2
3
+ Version: 2.0.3
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
@@ -166,6 +166,14 @@ class AutotileTemplateApplier:
166
166
  if not ts:
167
167
  return
168
168
 
169
+ # Check if a group is selected
170
+ if self.designer.selected_group_idx == -1:
171
+ print("Template Error: No group selected. Please select a group first.")
172
+ return
173
+
174
+ # Use the currently selected group
175
+ target_group = self.designer.groups[self.designer.selected_group_idx]
176
+
169
177
  rx, ry, rw, rh = tile_selector.selected_tile
170
178
  tile_w, tile_h = self.designer.editor.tilemap.tile_size
171
179
  sheet_cols = ts.surface.get_width() // tile_w
@@ -173,24 +181,6 @@ class AutotileTemplateApplier:
173
181
  start_col = rx // tile_w
174
182
  start_row = ry // tile_h
175
183
 
176
- # Define a unique group ID for THIS specific 3x3 selection area
177
- # (e.g. "GrassSet_12_4")
178
- set_id = f"{ts.name}_{start_col}_{start_row}"
179
-
180
- from .autotiler import AutotileGroup
181
-
182
- # Create a real group object in the designer
183
- target_group = None
184
- for g in self.designer.groups:
185
- if g.name == set_id:
186
- target_group = g
187
- break
188
-
189
- if not target_group:
190
- target_group = AutotileGroup(set_id)
191
- self.designer.groups.append(target_group)
192
- self.designer.selected_group_idx = len(self.designer.groups) - 1
193
-
194
184
  added_count = 0
195
185
  updated_count = 0
196
186
 
@@ -204,32 +194,43 @@ class AutotileTemplateApplier:
204
194
 
205
195
  ts_index = tile_selector.active_idx
206
196
 
207
- # Check for existing rule in THIS group
197
+ # Check for existing rule in THIS group with EXACT same neighbors and tileset
208
198
  matched_rule = None
209
199
  for r in target_group.rules:
200
+ # Must match both neighbors AND tileset to be considered the same rule
210
201
  if r.neighbors == neighbors and r.tileset_index == ts_index:
211
202
  matched_rule = r
212
203
  break
213
204
 
214
205
  if matched_rule:
206
+ # Only add variant if it's not already there
215
207
  if vid not in matched_rule.variant_ids:
216
208
  matched_rule.variant_ids.append(vid)
217
209
  updated_count += 1
218
210
  else:
219
- rule_name = f"{ts.name}_{start_col}_{start_row}_{rel_col}_{rel_row}"
211
+ # Create a new rule with a unique name
212
+ rule_num = len(target_group.rules) + 1
213
+ rule_name = f"Rule {rule_num}"
214
+
215
+ # Ensure unique name
216
+ existing_names = {r.name for r in target_group.rules}
217
+ while rule_name in existing_names:
218
+ rule_num += 1
219
+ rule_name = f"Rule {rule_num}"
220
+
220
221
  new_rule = AutotileRule(
221
222
  name=rule_name,
222
- neighbors=neighbors,
223
+ neighbors=set(neighbors), # Create a new set to avoid reference issues
223
224
  tileset_path=str(ts.path),
224
225
  variant_ids=[vid],
225
226
  tileset_index=ts_index,
226
- group_id=set_id,
227
+ group_id=target_group.name,
227
228
  )
228
229
  target_group.rules.append(new_rule)
229
230
  added_count += 1
230
231
 
231
232
  print(
232
- f"Template Applied: {template.name}. Group: {set_id}. {added_count} rules added, {updated_count} rules updated."
233
+ f"Template Applied: {template.name} to Group '{target_group.name}'. {added_count} rules added, {updated_count} rules updated."
233
234
  )
234
235
 
235
236
  def draw(self, screen: Surface):
@@ -87,7 +87,7 @@ class AutotileGroup:
87
87
  class AutotileRuleDesigner:
88
88
  def __init__(self, editor: "Editor", x: int, y: int):
89
89
  self.editor = editor
90
- self.rect = Rect(x, y, 600, 450)
90
+ self.rect = Rect(x, y, 600, 500) # Increased height to accommodate buttons outside list areas
91
91
  self.header_height = 30
92
92
 
93
93
  self.visible = False
@@ -102,7 +102,7 @@ class AutotileRuleDesigner:
102
102
  self.rename_text: str = ""
103
103
 
104
104
  self.scroll_offset: int = 0
105
- self.max_visible_rules: int = 10
105
+ self.max_visible_rules: int = 6 # Reduced to make scrolling more visible
106
106
  self.scroll_bar_rect: Optional[Rect] = None
107
107
  self.is_scrollbar_dragging: bool = False
108
108
 
@@ -139,9 +139,16 @@ class AutotileRuleDesigner:
139
139
  body_h = self.rect.height - self.header_height
140
140
  sidebar_w = 200
141
141
  self.list_area = Rect(self.rect.x, body_y, sidebar_w, body_h)
142
- self.group_list_area = Rect(self.rect.x, body_y, sidebar_w, body_h // 2)
142
+
143
+ # Reserve space for buttons at the bottom of each list area
144
+ button_h = 30
145
+ group_content_h = (body_h // 2) - button_h
146
+ rule_content_h = (body_h // 2) - button_h
147
+
148
+ # Group list area excludes the button
149
+ self.group_list_area = Rect(self.rect.x, body_y, sidebar_w, group_content_h)
143
150
  self.rule_list_area = Rect(
144
- self.rect.x, body_y + body_h // 2, sidebar_w, body_h // 2
151
+ self.rect.x, body_y + body_h // 2, sidebar_w, rule_content_h
145
152
  )
146
153
 
147
154
  self.edit_area = Rect(
@@ -154,15 +161,16 @@ class AutotileRuleDesigner:
154
161
  self.save_btn_rect = Rect(cx - btn_w - 5, btn_y, btn_w, 30)
155
162
  self.delete_btn_rect = Rect(cx + 5, btn_y, btn_w, 30)
156
163
 
164
+ # Position buttons OUTSIDE their respective list areas to prevent click conflicts
157
165
  self.new_group_btn_rect = Rect(
158
166
  self.group_list_area.x + 10,
159
- self.group_list_area.bottom - 35,
167
+ self.group_list_area.bottom + 5, # Below the group list area
160
168
  self.group_list_area.width - 20,
161
169
  25,
162
170
  )
163
171
  self.new_rule_btn_rect = Rect(
164
172
  self.rule_list_area.x + 10,
165
- self.rule_list_area.bottom - 30,
173
+ self.rule_list_area.bottom + 5, # Below the rule list area
166
174
  self.rule_list_area.width - 20,
167
175
  25,
168
176
  )
@@ -277,10 +285,16 @@ class AutotileRuleDesigner:
277
285
  )
278
286
  return True
279
287
 
288
+ # Check buttons FIRST before checking list areas (since buttons are outside)
280
289
  if self.new_group_btn_rect.collidepoint(mouse_pos):
281
290
  self._create_new_group_with_focus()
282
291
  return True
292
+
293
+ if self.new_rule_btn_rect.collidepoint(mouse_pos):
294
+ self._reset_selection()
295
+ return True
283
296
 
297
+ # Then check list areas
284
298
  if self.group_list_area.collidepoint(mouse_pos):
285
299
  self._handle_group_list_click(mouse_pos)
286
300
  return True
@@ -289,28 +303,18 @@ class AutotileRuleDesigner:
289
303
  self._handle_rule_list_click(mouse_pos)
290
304
  return True
291
305
 
292
- if self.save_btn_rect.collidepoint(mouse_pos):
293
- self._save_current_rule()
294
- return True
295
- if self.save_btn_rect.inflate(0, 40).collidepoint(mouse_pos):
296
- pass
297
-
298
- if self.new_rule_btn_rect.collidepoint(mouse_pos):
299
- self._reset_selection()
300
- return True
301
-
306
+ # Edit area interactions
302
307
  if self.edit_area.collidepoint(mouse_pos):
303
- if self._handle_grid_click(mouse_pos):
304
- return True
305
-
306
- if self.delete_btn_rect.collidepoint(mouse_pos):
307
- self._delete_current_rule()
308
- return True
309
- if self.external_btn_rect.collidepoint(mouse_pos):
310
- self._launch_external_viewer()
311
- return True
312
- if self.template_btn_rect.collidepoint(mouse_pos):
313
- self.template_manager.show_at(mouse_pos)
308
+ if self.save_btn_rect.collidepoint(mouse_pos):
309
+ self._save_current_rule()
310
+ elif self.delete_btn_rect.collidepoint(mouse_pos):
311
+ self._delete_current_rule()
312
+ elif self.external_btn_rect.collidepoint(mouse_pos):
313
+ self._launch_external_viewer()
314
+ elif self.template_btn_rect.collidepoint(mouse_pos):
315
+ self.template_manager.show_at(mouse_pos)
316
+ else:
317
+ self._handle_grid_click(mouse_pos)
314
318
  return True
315
319
 
316
320
  elif event.type == pygame.MOUSEBUTTONUP:
@@ -323,10 +327,8 @@ class AutotileRuleDesigner:
323
327
  self._update_layout()
324
328
  return True
325
329
 
326
- if not self.rect.collidepoint(mouse_pos) and not self.is_dragging:
327
- return False
328
-
329
- return True
330
+ # Always consume events when visible to prevent clicks bleeding through
331
+ return self.rect.collidepoint(mouse_pos) or self.is_dragging
330
332
 
331
333
  def _sync_last_editor_state(self):
332
334
  tile_selector = getattr(self.editor, "tileset_widget", None)
@@ -430,10 +432,6 @@ class AutotileRuleDesigner:
430
432
  return
431
433
 
432
434
  def _handle_rule_list_click(self, mouse_pos):
433
- if self.new_rule_btn_rect.collidepoint(mouse_pos):
434
- self._reset_selection()
435
- return
436
-
437
435
  if self.selected_group_idx == -1:
438
436
  return
439
437
 
@@ -441,6 +439,17 @@ class AutotileRuleDesigner:
441
439
  start_y = self.rule_list_area.y + 25
442
440
  item_h = 25
443
441
 
442
+ # Only process clicks within the visible list area
443
+ list_content_area = Rect(
444
+ self.rule_list_area.x,
445
+ self.rule_list_area.y + 25,
446
+ self.rule_list_area.width,
447
+ self.rule_list_area.height - 25 # Adjusted since button is outside
448
+ )
449
+
450
+ if not list_content_area.collidepoint(mouse_pos):
451
+ return
452
+
444
453
  visible_start = self.scroll_offset
445
454
  visible_end = min(visible_start + self.max_visible_rules, len(group.rules))
446
455
 
@@ -684,6 +693,7 @@ class AutotileRuleDesigner:
684
693
  d_name = name if len(name) < 22 else name[:19] + ".."
685
694
  screen.blit(self.font.render(d_name, True, TEXT_COLOR), (r.x + 5, r.y + 5))
686
695
 
696
+ # Draw button OUTSIDE the group list area
687
697
  pygame.draw.rect(
688
698
  screen, (80, 120, 80), self.new_group_btn_rect, border_radius=4
689
699
  )
@@ -700,6 +710,7 @@ class AutotileRuleDesigner:
700
710
 
701
711
  self._draw_scrollable_rule_list(screen)
702
712
 
713
+ # Draw button OUTSIDE the rule list area
703
714
  pygame.draw.rect(
704
715
  screen, (70, 130, 180), self.new_rule_btn_rect, border_radius=4
705
716
  )
@@ -709,7 +720,7 @@ class AutotileRuleDesigner:
709
720
  )
710
721
 
711
722
  def _draw_scrollable_rule_list(self, screen: Surface) -> None:
712
- """Draw rules with scroll indicators and scrollbar"""
723
+ """Draw rules with scroll indicators and scrollbar - with clipping to prevent overflow"""
713
724
  if self.selected_group_idx == -1:
714
725
  return
715
726
 
@@ -720,6 +731,17 @@ class AutotileRuleDesigner:
720
731
 
721
732
  self.scroll_offset = max(0, min(self.scroll_offset, max_scroll))
722
733
 
734
+ # Set clip rect to prevent rules from rendering outside the list area
735
+ # Since button is now outside, we can use more of the area
736
+ list_clip = Rect(
737
+ self.rule_list_area.x,
738
+ self.rule_list_area.y + 25,
739
+ self.rule_list_area.width,
740
+ self.rule_list_area.height - 25 # Just leave space for header
741
+ )
742
+ old_clip = screen.get_clip()
743
+ screen.set_clip(list_clip)
744
+
723
745
  visible_start = self.scroll_offset
724
746
  visible_end = min(visible_start + self.max_visible_rules, total_rules)
725
747
 
@@ -741,6 +763,9 @@ class AutotileRuleDesigner:
741
763
  d_name = rule.name if len(rule.name) < 20 else rule.name[:17] + ".."
742
764
  screen.blit(self.font.render(d_name, True, TEXT_COLOR), (r.x + 5, r.y + 5))
743
765
 
766
+ # Restore clip
767
+ screen.set_clip(old_clip)
768
+
744
769
  if total_rules > self.max_visible_rules:
745
770
  if self.scroll_offset > 0:
746
771
  arrow_up = "▲"
@@ -755,11 +780,11 @@ class AutotileRuleDesigner:
755
780
  arrow_surf = self.font.render(arrow_down, True, (150, 200, 255))
756
781
  screen.blit(
757
782
  arrow_surf,
758
- (self.rule_list_area.right - 20, self.rule_list_area.bottom - 35),
783
+ (self.rule_list_area.right - 20, self.rule_list_area.bottom - 20),
759
784
  )
760
785
 
761
786
  scrollbar_height = 60
762
- track_height = self.rule_list_area.height - 70
787
+ track_height = self.rule_list_area.height - 50
763
788
  scroll_ratio = self.scroll_offset / max_scroll if max_scroll > 0 else 0
764
789
 
765
790
  scrollbar_y = (
@@ -812,7 +837,7 @@ class AutotileRuleDesigner:
812
837
  elif event.type == pygame.MOUSEMOTION:
813
838
  if self.is_scrollbar_dragging:
814
839
  relative_y = event.pos[1] - self.rule_list_area.y - 25
815
- track_height = self.rule_list_area.height - 70
840
+ track_height = self.rule_list_area.height - 50 # Updated to match drawing
816
841
 
817
842
  if track_height > 0:
818
843
  scroll_ratio = max(0, min(1, relative_y / track_height))
File without changes
File without changes
File without changes