diff --git a/scripts/dashboard_grid.gd b/scripts/dashboard_grid.gd index f41b416..7e0ddbb 100644 --- a/scripts/dashboard_grid.gd +++ b/scripts/dashboard_grid.gd @@ -168,6 +168,7 @@ func _end_drag(mouse_pos: Vector2) -> void: return _clear_hover() + _drag_module.modulate = Color(1, 1, 1, 1) var cell_pos := _cell_at_position(mouse_pos) var target_col := cell_pos.x @@ -175,22 +176,18 @@ func _end_drag(mouse_pos: Vector2) -> void: # Dropped on the same cell — just put it back if target_col == _drag_source_col and target_row == _drag_source_row: - _drag_module.modulate = Color(1, 1, 1, 1) _place_module_in_cell(_drag_module, target_col, target_row) module_placed.emit(_drag_module, target_col, target_row) - _drag_module = null - _is_dragging = false - _drag_source_col = -1 - _drag_source_row = -1 + _finish_drag() return - # Remove the module from the grid (we'll reparent it below) + # Remove the module from its temporary home (the grid itself) _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) if existing != null: - # Swap: put existing module in source cell, drag module in target + # SWAP: put existing module in source cell, drag module in target existing.get_parent().remove_child(existing) var was_source_valid := _is_valid_cell(_drag_source_col, _drag_source_row) if was_source_valid: @@ -199,19 +196,29 @@ func _end_drag(mouse_pos: Vector2) -> void: else: existing.queue_free() - # Place dragged module in target cell - _drag_module.modulate = Color(1, 1, 1, 1) - _place_module_in_cell(_drag_module, target_col, target_row) - module_placed.emit(_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) + else: + # INSERT: flatten all modules, insert dragged one at target, reflow + var all_modules := _collect_all_modules() + + var insert_index := target_row * columns + target_col + insert_index = clampi(insert_index, 0, all_modules.size()) + all_modules.insert(insert_index, _drag_module) + + _reflow_modules(all_modules) else: # Dropped outside the grid — return to source if still valid if _is_valid_cell(_drag_source_col, _drag_source_row): - _drag_module.modulate = Color(1, 1, 1, 1) _place_module_in_cell(_drag_module, _drag_source_col, _drag_source_row) module_placed.emit(_drag_module, _drag_source_col, _drag_source_row) else: _drag_module.queue_free() + _finish_drag() + + +func _finish_drag() -> void: _drag_module = null _is_dragging = false _drag_source_col = -1 @@ -281,6 +288,69 @@ func _set_cell_child(cell: PanelContainer, child: Control) -> void: cell.add_child(child) +# -------------------------------------------------------------------------- +# List-reflow helpers +# -------------------------------------------------------------------------- + +# Returns all modules in the grid, in row-major order (left-to-right, top-to-bottom). +func _collect_all_modules() -> Array[Control]: + var result: Array[Control] = [] + for r in range(rows): + for c in range(columns): + var mod := _get_cell_module(c, r) + if mod != null: + result.append(mod) + return result + + +# Clears every cell, then places the given modules in row-major order, +# adding extra rows as needed to fit them all. +func _reflow_modules(modules: Array[Control]) -> void: + # Remove all modules from cells + for r in range(rows): + for c in range(columns): + _take_module_from_cell(c, r) + + if columns == 0: + return + + # Ensure enough rows exist + var needed := maxi(rows, ceili(float(modules.size()) / float(columns))) + while _cells.size() < needed: + _add_row() + rows = _cells.size() + + # Place modules sequentially + for i in range(modules.size()): + var c := i % columns + var r := floori(i / columns) + _place_module_in_cell(modules[i], c, r) + module_placed.emit(modules[i], c, r) + + +func _add_row() -> void: + var new_row := _cells.size() + _cells.append([]) + for col in range(columns): + var cell := PanelContainer.new() + cell.name = "Cell_%d_%d" % [col, new_row] + cell.mouse_filter = Control.MOUSE_FILTER_PASS + _style_cell(cell, col, new_row) + add_child(cell) + _cells[new_row].append(cell) + rows = _cells.size() + _layout_cells() + + +func _count_modules_in_grid() -> int: + var count := 0 + for r in range(rows): + for c in range(columns): + if _get_cell_module(c, r) != null: + count += 1 + return count + + # -------------------------------------------------------------------------- # Grid rebuild # -------------------------------------------------------------------------- @@ -313,7 +383,12 @@ func _rebuild_grid() -> void: return var new_cols := maxi(1, int(avail.x / (cell_min_size.x + cell_spacing))) - var new_rows := maxi(1, int(avail.y / (cell_min_size.y + cell_spacing))) + + # Ensure enough rows for all current modules after column change + var module_count := _count_modules_in_grid() + var min_rows := maxi(1, ceili(float(module_count) / float(new_cols))) + var avail_rows := maxi(1, int(avail.y / (cell_min_size.y + cell_spacing))) + var new_rows := maxi(avail_rows, min_rows) if new_cols == columns and new_rows == rows and _cells.size() > 0: _layout_cells() @@ -419,20 +494,33 @@ func _save_modules() -> void: func _restore_modules() -> void: + # Collect modules in insertion order (row-major from _save_modules) + var module_list: Array[Control] = [] for key in _module_positions: - var parts: PackedStringArray = key.split(",") - var col: int = int(parts[0]) - var row := int(parts[1]) - var module: Control = _module_positions[key] - - if _is_valid_cell(col, row): - _place_module_in_cell(module, col, row) - else: - if _cells.size() > 0 and _cells[0].size() > 0: - var last_row := _cells.size() - 1 - var last_col: int = _cells[last_row].size() - 1 - _place_module_in_cell(module, last_col, last_row) - else: - module.queue_free() + module_list.append(_module_positions[key]) _module_positions.clear() + + if module_list.is_empty(): + return + + # Reflow onto the new grid dimensions + var module_count := module_list.size() + var needed := maxi(rows, ceili(float(module_count) / float(columns))) + while _cells.size() < needed: + var new_row := _cells.size() + _cells.append([]) + for col in range(columns): + var cell := PanelContainer.new() + cell.name = "Cell_%d_%d" % [col, new_row] + cell.mouse_filter = Control.MOUSE_FILTER_PASS + _style_cell(cell, col, new_row) + add_child(cell) + _cells[new_row].append(cell) + rows = _cells.size() + + for i in range(module_list.size()): + var c := i % columns + var r := floori(i / columns) + if _is_valid_cell(c, r): + _place_module_in_cell(module_list[i], c, r)