refactor: sticky reclaim state unified into fewer functions

This commit is contained in:
tiger tiger tiger 2026-01-16 23:55:24 +01:00
parent 6def38a166
commit 0b1c726c2e
2 changed files with 88 additions and 140 deletions

View File

@ -206,7 +206,7 @@ func populate_board(card_names: Array[StringName]):
# marking the first card as random picks # marking the first card as random picks
new_card.picked_random = new_card.name == card_names[1] new_card.picked_random = new_card.name == card_names[1]
for new_sticky_note: StickyNote in all_new["sticky_notes"]: # spawning a sticky note for new_sticky_note: StickyNote in all_new["sticky_notes"]: # spawning a sticky note
add_sticky_note(new_sticky_note, false) reclaim_sticky_to_panel(new_sticky_note, false)
# marking the first sticky as random picks # marking the first sticky as random picks
new_sticky_note.picked_random = new_sticky_note.name == card_names[3] new_sticky_note.picked_random = new_sticky_note.name == card_names[3]
@ -265,13 +265,39 @@ func add_card(card: Card, re_parent:bool = true):
card.set_owner(self) card.set_owner(self)
card.is_dragable = true card.is_dragable = true
func add_sticky_note(sticky: StickyNote, re_parent:bool = true): ## Unified function to reclaim any sticky note to a panel
var new_panel := StickyNotePanel.new() ## Handles ALL scenarios: initial setup, drag-drop, exchanges, keyboard navigation
sticky_note_container.add_child(new_panel, true, Node.INTERNAL_MODE_DISABLED) func reclaim_sticky_to_panel(sticky: StickyNote, animate: bool = true, prefer_panel_index: int = -1) -> void:
#WARNING this for some reason would break the tweens # Find or create target panel
new_panel.set_owner(self) var target_panel: StickyNotePanel = null
sticky.current_handle = self
new_panel.attatch_sticky_note(sticky, self, false, re_parent) # Try preferred panel first (for exchanges)
if prefer_panel_index >= 0 and prefer_panel_index < sticky_note_container.get_child_count():
var panel = sticky_note_container.get_child(prefer_panel_index)
if panel is StickyNotePanel and panel.is_empty():
target_panel = panel
# Find any empty panel
if not target_panel:
for panel in sticky_note_container.get_children():
if panel is StickyNotePanel and panel.is_empty():
target_panel = panel
break
# Create new panel if needed
if not target_panel:
target_panel = StickyNotePanel.new()
sticky_note_container.add_child(target_panel, true, Node.INTERNAL_MODE_DISABLED)
target_panel.set_owner(self)
# Attach sticky to panel (handles all state setup and animation)
target_panel.attatch_sticky_note(sticky, self, animate)
# Clean up other empty panels
if animate: # Only clean up during interactive use, not initial setup
for panel in sticky_note_container.get_children():
if panel is StickyNotePanel and panel != target_panel:
panel.clear_if_empty()
# Checks if a Node is currently inside the dropzone # Checks if a Node is currently inside the dropzone
func is_in_dropzone(to_check: Node) -> bool: func is_in_dropzone(to_check: Node) -> bool:
@ -316,13 +342,17 @@ func _end_drag(draggable: Draggable) -> void:
# Handle exchange result (sticky swapped with card's sticky) # Handle exchange result (sticky swapped with card's sticky)
if result == Draggable.DropResult.EXCHANGED: if result == Draggable.DropResult.EXCHANGED:
_handle_sticky_exchange(draggable, drop_target) var old_sticky = (drop_target as Card).get_last_exchanged_sticky()
if old_sticky:
# Return exchanged sticky to panel where new one came from (if applicable)
var prefer_panel = current_sticky_note_id if (draggable as StickyNote)._came_from_panel else -1
reclaim_sticky_to_panel(old_sticky, true, prefer_panel)
# If sticky was dropped on board (not card), reclaim it to panel with animation # If sticky was dropped on board (not card), reclaim it to panel with animation
elif result == Draggable.DropResult.ACCEPTED and draggable is StickyNote and drop_target == self: elif result == Draggable.DropResult.ACCEPTED and draggable is StickyNote and drop_target == self:
_reclaim_sticky_to_panel(draggable) reclaim_sticky_to_panel(draggable, true)
elif draggable is StickyNote and not is_in_dropzone(draggable): elif draggable is StickyNote and not is_in_dropzone(draggable):
# Sticky dropped in panel area but no empty panel found - reclaim to panel # Sticky dropped in panel area - reclaim to panel with animation
_reclaim_sticky_to_panel(draggable) reclaim_sticky_to_panel(draggable, true)
else: else:
# Fallback: use default board drop (for cards) # Fallback: use default board drop (for cards)
handle_drop(draggable) handle_drop(draggable)
@ -337,85 +367,6 @@ func _end_drag(draggable: Draggable) -> void:
check_board_comnpletion() check_board_comnpletion()
## Handles the exchange when a sticky is dropped on a card that already has one
## The exchanged sticky always goes to the sticky_note_container (panel zone)
func _handle_sticky_exchange(new_sticky: StickyNote, card: Card) -> void:
var old_sticky = card.get_last_exchanged_sticky()
if not old_sticky:
push_warning("CardBoard: Exchange occurred but no sticky returned")
return
# Reset visual state for old sticky
old_sticky.rotation = 0.0
old_sticky.scale = Vector2.ONE
old_sticky.z_index = 0
# Exchanged sticky always goes to sticky_note_container with smooth animation
if new_sticky._came_from_panel and sticky_note_container.get_child_count() > 0:
# New sticky came from panel - return old sticky to that panel (swap positions)
var target_panel = sticky_note_container.get_child(current_sticky_note_id)
old_sticky.reparent(dropzone)
old_sticky.on_board = true
old_sticky.attached_to = dropzone.get_parent() # Detach from card, attach to board temporarily
target_panel.attached_sticky_note = old_sticky
# Use reclaim to smoothly animate the sticky back to the panel
target_panel.reclaim_sticky_note()
else:
# New sticky was loose - reclaim old sticky to new panel with animation
_reclaim_sticky_to_panel(old_sticky)
# Clean up empty panel if the new sticky came from one
if new_sticky._came_from_panel and sticky_note_container.get_child_count() > 0:
sticky_note_container.get_child(current_sticky_note_id).clear_if_empty()
## Smoothly reclaims a sticky note to a panel (creates one if needed)
func _reclaim_sticky_to_panel(sticky: StickyNote) -> void:
var target_panel: StickyNotePanel = null
# Try to find or create an appropriate panel
if sticky._came_from_panel and current_sticky_note_id < sticky_note_container.get_child_count():
# Try to use the panel the sticky came from
var original_panel = sticky_note_container.get_child(current_sticky_note_id)
if original_panel is StickyNotePanel and original_panel.is_empty():
target_panel = original_panel
# If no reusable panel, find any empty one
if not target_panel:
for panel in sticky_note_container.get_children():
if panel is StickyNotePanel and panel.is_empty():
target_panel = panel
break
# Create new panel if needed
if not target_panel:
target_panel = StickyNotePanel.new()
sticky_note_container.add_child(target_panel, true, Node.INTERNAL_MODE_DISABLED)
target_panel.set_owner(self)
# Ensure sticky is in dropzone temporarily (for smooth animation from board to panel)
if sticky.get_parent() != dropzone:
sticky.reparent(dropzone)
sticky.on_board = true
sticky.attached_to = self
sticky.current_handle = self
# Reset visual state
sticky.rotation = 0.0
sticky.scale = Vector2.ONE
sticky.z_index = 0
# Set panel reference and trigger smooth reclaim animation
target_panel.attached_sticky_note = sticky
target_panel.reclaim_sticky_note()
# Clean up other empty panels
for panel in sticky_note_container.get_children():
if panel is StickyNotePanel and panel != target_panel:
panel.clear_if_empty()
## Updates focus and navigation state after a drop ## Updates focus and navigation state after a drop
func _update_focus_after_drop(draggable: Draggable) -> void: func _update_focus_after_drop(draggable: Draggable) -> void:
# Update focus based on where the item ended up # Update focus based on where the item ended up
@ -427,7 +378,10 @@ func _update_focus_after_drop(draggable: Draggable) -> void:
func _return_sticky_notes_to_panels() -> void: func _return_sticky_notes_to_panels() -> void:
if not (current_context == ASSIGN and focus_stickies): return #FIXME this is an early return to prevent race conditions. Check if it is save to be removed. if not (current_context == ASSIGN and focus_stickies): return #FIXME this is an early return to prevent race conditions. Check if it is save to be removed.
for panel:StickyNotePanel in sticky_note_container.get_children(): for panel:StickyNotePanel in sticky_note_container.get_children():
panel.reclaim_sticky_note() if panel is StickyNotePanel and panel.attached_sticky_note:
# Reclaim if sticky is not already in the panel
if panel.attached_sticky_note.get_parent() != panel:
panel.attatch_sticky_note(panel.attached_sticky_note, self, true)
for node in dropzone.get_children(): for node in dropzone.get_children():
if node is StickyNote: if node is StickyNote:
@ -633,7 +587,9 @@ func _input(event) -> void:
if current_context == NAVIGATE: if current_context == NAVIGATE:
focus_stickies = true focus_stickies = true
elif current_context == ASSIGN: elif current_context == ASSIGN:
sticky_note_container.get_children()[current_sticky_note_id].reclaim_sticky_note() var panel = sticky_note_container.get_children()[current_sticky_note_id]
if panel is StickyNotePanel and panel.attached_sticky_note:
reclaim_sticky_to_panel(panel.attached_sticky_note, true, current_sticky_note_id)
current_context = NAVIGATE current_context = NAVIGATE
get_viewport().set_input_as_handled() get_viewport().set_input_as_handled()
@ -682,14 +638,14 @@ func _input(event) -> void:
current_dropzone_id = find_first_free_card() current_dropzone_id = find_first_free_card()
else: else:
if currently_active_node is StickyNote: if currently_active_node is StickyNote:
add_sticky_note(currently_active_node) reclaim_sticky_to_panel(currently_active_node, true)
current_sticky_note_id = sticky_note_container.get_child_count()-1 current_sticky_note_id = sticky_note_container.get_child_count()-1
current_context = ASSIGN current_context = ASSIGN
focus_stickies = false focus_stickies = false
if currently_active_node is Card: if currently_active_node is Card:
if currently_active_node.has_sticky_note_attached(): if currently_active_node.has_sticky_note_attached():
currently_active_node = currently_active_node.remove_sticky_note() currently_active_node = currently_active_node.remove_sticky_note()
add_sticky_note(currently_active_node) reclaim_sticky_to_panel(currently_active_node, true)
current_sticky_note_id = sticky_note_container.get_child_count()-1 current_sticky_note_id = sticky_note_container.get_child_count()-1
focus_stickies = true focus_stickies = true
else: else:
@ -847,7 +803,7 @@ func initialise_from_save(savegame: SaveGame) -> void:
for sticky: StickyNote in card_pile["sticky_notes"]: for sticky: StickyNote in card_pile["sticky_notes"]:
# Check if sticky is in panel # Check if sticky is in panel
if savegame.board_in_panel.has(sticky.name): if savegame.board_in_panel.has(sticky.name):
add_sticky_note(sticky, false) reclaim_sticky_to_panel(sticky, false)
print_debug(" Sticky '%s' added to panel" % sticky.name) print_debug(" Sticky '%s' added to panel" % sticky.name)
# Check if sticky is attached to a card # Check if sticky is attached to a card
@ -862,7 +818,7 @@ func initialise_from_save(savegame: SaveGame) -> void:
print_debug(" Sticky '%s' attached to card '%s'" % [sticky.name, card_name]) print_debug(" Sticky '%s' attached to card '%s'" % [sticky.name, card_name])
else: else:
push_warning("CardBoard: Sticky '%s' attached to non-existent card '%s', adding to panel" % [sticky.name, card_name]) push_warning("CardBoard: Sticky '%s' attached to non-existent card '%s', adding to panel" % [sticky.name, card_name])
add_sticky_note(sticky, false) reclaim_sticky_to_panel(sticky, false)
# Sticky is loose on board # Sticky is loose on board
else: else:

View File

@ -18,35 +18,50 @@ func _ready():
custom_minimum_size = Vector2(custom_minimum_size.x, 0) custom_minimum_size = Vector2(custom_minimum_size.x, 0)
var is_attatching: bool = false var is_attatching: bool = false
func attatch_sticky_note(attatchment: StickyNote, custom_owner: Node, tween:bool = true, re_parent:bool = true): func attatch_sticky_note(attatchment: StickyNote, custom_owner: Node, animate:bool = true):
is_attatching = true is_attatching = true
attatchment.on_board = false
attached_sticky_note = attatchment attached_sticky_note = attatchment
attatchment.attached_to = null attatchment.current_handle = custom_owner
if tween: attatchment.owner = custom_owner
await get_tree().process_frame
# Expand panel height
if animate:
var height_tween: Tween = create_tween() var height_tween: Tween = create_tween()
height_tween.tween_property(self, "custom_minimum_size", minimum_size, 0.1) height_tween.tween_property(self, "custom_minimum_size", minimum_size, 0.1)
var target_post := get_global_transform().origin+ancor_position
for panel: StickyNotePanel in get_parent().get_children():
if panel.attached_sticky_note == attatchment and panel.get_index() < get_index():
target_post = get_global_transform().origin+ancor_position - Vector2(0, minimum_size.y)
attatchment.tween_transform_to(Transform2D(0, target_post))
await attatchment.transform_tween_finished
await get_tree().process_frame
attatchment.reparent(self)
attatchment.position = ancor_position
else: else:
custom_minimum_size = minimum_size custom_minimum_size = minimum_size
if re_parent:
# Position sticky
if animate:
await get_tree().process_frame
attatchment.on_board = false
attatchment.z_index = 125 # On top during animation
# Reparent while keeping world position for smooth animation
attatchment.reparent(self, true)
attatchment.attached_to = self
# Tween to anchor position in panel's coordinate space
var tween := create_tween().set_ease(Tween.EASE_IN_OUT).set_trans(Tween.TRANS_BACK)
tween.tween_property(attatchment, "position", ancor_position, 0.7)
tween.tween_property(attatchment, "rotation", 0.0, 0.7)
tween.parallel().tween_property(attatchment, "scale", Vector2.ONE, 0.7)
await tween.finished
attatchment.z_index = 0
else:
# Immediate placement (for initial board setup)
if attatchment.get_parent():
attatchment.reparent(self) attatchment.reparent(self)
else: else:
add_child(attatchment) add_child(attatchment)
attatchment.on_board = false
attatchment.attached_to = self
attatchment.position = ancor_position attatchment.position = ancor_position
attatchment.rotation = 0.0
attatchment.scale = Vector2.ONE
is_attatching = false is_attatching = false
attatchment.owner = custom_owner
attatchment.attached_to = self
attatchment.current_handle = custom_owner
var is_gapped: bool = false var is_gapped: bool = false
@ -69,29 +84,6 @@ func collapse_gap():
var height_tween: Tween = create_tween() var height_tween: Tween = create_tween()
height_tween.tween_property(self, "custom_minimum_size", minimum_size, 0.1) height_tween.tween_property(self, "custom_minimum_size", minimum_size, 0.1)
func reclaim_sticky_note() -> bool:
# Don't reclaim if sticky is already attached to this panel (prevents double reclaim)
if is_empty() and attached_sticky_note.attached_to != self and attached_sticky_note.attached_to is not Card:
is_attatching = true
attached_sticky_note.on_board = false
attached_sticky_note.z_index = 125 # Make sure it's on top of all other stickies'
# Reparent while keeping world position (global transform)
attached_sticky_note.reparent(self, true)
attached_sticky_note.attached_to = self
attached_sticky_note.owner = self.owner
# Tween from current position to target anchor position in panel's coordinate space
var tween := create_tween().set_ease(Tween.EASE_IN_OUT).set_trans(Tween.TRANS_BACK)
tween.tween_property(attached_sticky_note, "position", ancor_position, 0.7)
await tween.finished
attached_sticky_note.z_index = 0
is_attatching = false
return true
return false
var invalid: bool = false var invalid: bool = false
func clear_if_empty(): func clear_if_empty():
if !is_empty(): return if !is_empty(): return
@ -122,8 +114,8 @@ func handle_drop(draggable: StickyNote) -> int:
if not can_accept_drop(draggable): if not can_accept_drop(draggable):
return Draggable.DropResult.REJECTED return Draggable.DropResult.REJECTED
# Attach sticky to this panel # Attach sticky to this panel with animation
attatch_sticky_note(draggable, owner, true, true) attatch_sticky_note(draggable, owner, true)
# Clean up other empty panels # Clean up other empty panels
for panel in get_parent().get_children(): for panel in get_parent().get_children():