hide grid cells by default, show during drag; fix frozen-on-gap with cell-bounds check and drop-to-source fallback

This commit is contained in:
Eric Smith 2026-05-20 14:54:11 -04:00
parent 6198fd556f
commit 92110cfee7

View file

@ -25,8 +25,8 @@ var _drag_mouse_offset: Vector2 = Vector2.ZERO
var _hover_col: int = -1 var _hover_col: int = -1
var _hover_row: int = -1 var _hover_row: int = -1
# Styles cached per-cell so we can modify them during drag # Visible cell styles cached per-cell so we can modify them during drag
var _cell_styles: Dictionary = {} # cell -> StyleBoxFlat var _cell_styles: Dictionary = {} # "col,row" -> StyleBoxFlat
func _ready() -> void: func _ready() -> void:
@ -127,10 +127,13 @@ func _begin_drag(mouse_pos: Vector2) -> void:
_drag_source_col = col _drag_source_col = col
_drag_source_row = row _drag_source_row = row
# Show grid cells so the user can see drop zones
_show_cells(true)
# Calculate where the module sits in grid coordinates before reparenting. # Calculate where the module sits in grid coordinates before reparenting.
# module.position is relative to the cell; cell.position is relative to us (the grid). # module.position is relative to the cell; cell.position is relative to us (the grid).
var cell: PanelContainer = _cells[row][col] var cell_ref: PanelContainer = _cells[row][col]
var module_origin_in_grid := cell.position + module.position var module_origin_in_grid := cell_ref.position + module.position
_drag_mouse_offset = mouse_pos - module_origin_in_grid _drag_mouse_offset = mouse_pos - module_origin_in_grid
_drag_module = _take_module_from_cell(col, row) _drag_module = _take_module_from_cell(col, row)
@ -168,26 +171,30 @@ func _end_drag(mouse_pos: Vector2) -> void:
return return
_clear_hover() _clear_hover()
_show_cells(false)
_drag_module.modulate = Color(1, 1, 1, 1) _drag_module.modulate = Color(1, 1, 1, 1)
var cell_pos := _cell_at_position(mouse_pos) var cell_pos := _cell_at_position(mouse_pos)
var target_col := cell_pos.x var target_col := cell_pos.x
var target_row := cell_pos.y var target_row := cell_pos.y
# Dropped on the same cell — just put it back # Check whether the mouse is actually inside the cell bounds (not in the gap)
if target_col == _drag_source_col and target_row == _drag_source_row: var on_cell := false
_place_module_in_cell(_drag_module, target_col, target_row) if _is_valid_cell(target_col, target_row):
module_placed.emit(_drag_module, target_col, target_row) var cell_ref: PanelContainer = _cells[target_row][target_col]
on_cell = Rect2(Vector2.ZERO, cell_ref.size).has_point(mouse_pos - cell_ref.position)
if not on_cell or not _is_valid_cell(target_col, target_row):
_drop_to_source()
_finish_drag() _finish_drag()
return return
# Remove the module from its temporary home (the grid itself) # Remove from its temporary parent (the grid)
_drag_module.get_parent().remove_child(_drag_module) _drag_module.get_parent().remove_child(_drag_module)
if _is_valid_cell(target_col, target_row):
var existing := _get_cell_module(target_col, target_row) var existing := _get_cell_module(target_col, target_row)
if existing != null: if existing != null:
# SWAP: put existing module in source cell, drag module in target # SWAP: put existing in source, dragged module in target
existing.get_parent().remove_child(existing) existing.get_parent().remove_child(existing)
var was_source_valid := _is_valid_cell(_drag_source_col, _drag_source_row) var was_source_valid := _is_valid_cell(_drag_source_col, _drag_source_row)
if was_source_valid: if was_source_valid:
@ -199,19 +206,23 @@ func _end_drag(mouse_pos: Vector2) -> void:
_place_module_in_cell(_drag_module, target_col, target_row) _place_module_in_cell(_drag_module, target_col, target_row)
module_placed.emit(_drag_module, target_col, target_row) module_placed.emit(_drag_module, target_col, target_row)
else: else:
# Place in the empty cell directly — you get full control of positioning # Place in the empty cell
_place_module_in_cell(_drag_module, target_col, target_row) _place_module_in_cell(_drag_module, target_col, target_row)
module_placed.emit(_drag_module, target_col, target_row) module_placed.emit(_drag_module, target_col, target_row)
else:
# Dropped outside the grid — return to source if still valid _finish_drag()
func _drop_to_source() -> void:
if _drag_module == null:
return
if _is_valid_cell(_drag_source_col, _drag_source_row): if _is_valid_cell(_drag_source_col, _drag_source_row):
_drag_module.get_parent().remove_child(_drag_module)
_place_module_in_cell(_drag_module, _drag_source_col, _drag_source_row) _place_module_in_cell(_drag_module, _drag_source_col, _drag_source_row)
module_placed.emit(_drag_module, _drag_source_col, _drag_source_row) module_placed.emit(_drag_module, _drag_source_col, _drag_source_row)
else: else:
_drag_module.queue_free() _drag_module.queue_free()
_finish_drag()
func _finish_drag() -> void: func _finish_drag() -> void:
_drag_module = null _drag_module = null
@ -235,16 +246,13 @@ func _highlight_cell(col: int, row: int, highlighted: bool) -> void:
if not _is_valid_cell(col, row): if not _is_valid_cell(col, row):
return return
var cell: PanelContainer = _cells[row][col] var cell: PanelContainer = _cells[row][col]
var style_key: String = "%d,%d" % [col, row] var style: StyleBoxFlat = cell.get_meta("visible_style")
var style: StyleBoxFlat = _cell_styles.get(style_key)
if style == null: if style == null:
return return
if highlighted: if highlighted:
style.border_color = Color(0.5, 0.6, 1.0, 1.0) style.border_color = Color(0.5, 0.6, 1.0, 1.0)
else: else:
style.border_color = Color(0.25, 0.25, 0.35, 1.0) style.border_color = Color(0.25, 0.25, 0.35, 1.0)
cell.add_theme_stylebox_override("panel", style)
cell.add_theme_stylebox_override("panel_focused", style)
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
@ -409,11 +417,12 @@ func _cancel_drag() -> void:
_place_module_in_cell(_drag_module, _drag_source_col, _drag_source_row) _place_module_in_cell(_drag_module, _drag_source_col, _drag_source_row)
else: else:
_drag_module.queue_free() _drag_module.queue_free()
_show_cells(false)
_clear_hover()
_drag_module = null _drag_module = null
_is_dragging = false _is_dragging = false
_drag_source_col = -1 _drag_source_col = -1
_drag_source_row = -1 _drag_source_row = -1
_clear_hover()
func _build_cells() -> void: func _build_cells() -> void:
@ -430,20 +439,48 @@ func _build_cells() -> void:
func _style_cell(cell: PanelContainer, col: int, row: int) -> void: func _style_cell(cell: PanelContainer, col: int, row: int) -> void:
var style := StyleBoxFlat.new() # Hidden style — used when not dragging
style.bg_color = Color(0.14, 0.14, 0.18, 1.0) var hidden := StyleBoxFlat.new()
style.border_width_left = 1 hidden.bg_color = Color.TRANSPARENT
style.border_width_top = 1 hidden.border_width_left = 1
style.border_width_right = 1 hidden.border_width_top = 1
style.border_width_bottom = 1 hidden.border_width_right = 1
style.border_color = Color(0.25, 0.25, 0.35, 1.0) hidden.border_width_bottom = 1
style.corner_radius_top_left = 6 hidden.border_color = Color.TRANSPARENT
style.corner_radius_top_right = 6 hidden.corner_radius_top_left = 6
style.corner_radius_bottom_right = 6 hidden.corner_radius_top_right = 6
style.corner_radius_bottom_left = 6 hidden.corner_radius_bottom_right = 6
hidden.corner_radius_bottom_left = 6
# Visible style — used during drag, may be modified by highlights
var visible := StyleBoxFlat.new()
visible.bg_color = Color(0.14, 0.14, 0.18, 1.0)
visible.border_width_left = 1
visible.border_width_top = 1
visible.border_width_right = 1
visible.border_width_bottom = 1
visible.border_color = Color(0.25, 0.25, 0.35, 1.0)
visible.corner_radius_top_left = 6
visible.corner_radius_top_right = 6
visible.corner_radius_bottom_right = 6
visible.corner_radius_bottom_left = 6
cell.add_theme_stylebox_override("panel", hidden)
cell.add_theme_stylebox_override("panel_focused", hidden)
cell.set_meta("hidden_style", hidden)
cell.set_meta("visible_style", visible)
_cell_styles["%d,%d" % [col, row]] = visible
func _show_cells(show: bool) -> void:
for row_idx in range(_cells.size()):
for col_idx in range(_cells[row_idx].size()):
var cell: PanelContainer = _cells[row_idx][col_idx]
if not is_instance_valid(cell):
continue
var style: StyleBoxFlat = cell.get_meta("visible_style") if show else cell.get_meta("hidden_style")
cell.add_theme_stylebox_override("panel", style) cell.add_theme_stylebox_override("panel", style)
cell.add_theme_stylebox_override("panel_focused", style) cell.add_theme_stylebox_override("panel_focused", style)
_cell_styles["%d,%d" % [col, row]] = style
func _teardown_cells() -> void: func _teardown_cells() -> void: