Compare commits

...

4 Commits

23 changed files with 355 additions and 411 deletions

View File

@@ -15,7 +15,7 @@ Added a proper opacity slider for sprites, with an "affect children" toggle. Cli
![Opacity Demo](https://github.com/user-attachments/assets/d5a05508-1cb1-46e0-8c90-d0aac73398db) ![Opacity Demo](https://github.com/user-attachments/assets/d5a05508-1cb1-46e0-8c90-d0aac73398db)
### <EFBFBD> Blend Modes ### 🎨 Blend Modes
Photoshop-style blend modes for sprites! Choose from Normal, Multiply, Screen, Overlay, Soft Light, Hard Light, Color Dodge, Color Burn, Darken, Lighten, Add, and Subtract. Perfect for lighting effects, shadows, or artistic styling. Photoshop-style blend modes for sprites! Choose from Normal, Multiply, Screen, Overlay, Soft Light, Hard Light, Color Dodge, Color Burn, Darken, Lighten, Add, and Subtract. Perfect for lighting effects, shadows, or artistic styling.
@@ -23,7 +23,7 @@ Photoshop-style blend modes for sprites! Choose from Normal, Multiply, Screen, O
* Works alongside opacity controls * Works alongside opacity controls
### <EFBFBD>🌀 Rotation Wobble ### 🌀 Rotation Wobble
Sprites can now do a subtle (or chaotic) rotational wobble. Frequency + amplitude sliders included. Sprites can now do a subtle (or chaotic) rotational wobble. Frequency + amplitude sliders included.

View File

@@ -280,7 +280,6 @@ func blinking():
blinkTick = -12 blinkTick = -12
func epicFail(err): func epicFail(err):
print(fail)
if fail == null: if fail == null:
return return

View File

@@ -12,10 +12,8 @@ signal sprite_joined_group(sprite_id: int, group_name: String)
signal sprite_left_group(sprite_id: int, group_name: String) signal sprite_left_group(sprite_id: int, group_name: String)
func _ready(): func _ready():
print("DEBUG: WobbleSyncManager._ready() called") # Manager initialization complete
print("DEBUG: WobbleSyncManager autoload initialized successfully") pass
print("DEBUG: Initial syncGroups: ", syncGroups)
print("DEBUG: Initial spriteGroupMembership: ", spriteGroupMembership)
class WobbleSyncGroup: class WobbleSyncGroup:
var name: String var name: String
@@ -32,8 +30,6 @@ class WobbleSyncGroup:
## Set wobble parameters for the entire group ## Set wobble parameters for the entire group
func setWobbleParameters(x_frq: float, x_amp: float, y_frq: float, y_amp: float, r_frq: float, r_amp: float, manager_ref): func setWobbleParameters(x_frq: float, x_amp: float, y_frq: float, y_amp: float, r_frq: float, r_amp: float, manager_ref):
print("DEBUG: WobbleSyncGroup.setWobbleParameters called for group '", name, "' with ", memberSprites.size(), " members")
print("DEBUG: Member sprite IDs: ", memberSprites)
xFrq = x_frq xFrq = x_frq
xAmp = x_amp xAmp = x_amp
yFrq = y_frq yFrq = y_frq
@@ -43,13 +39,10 @@ class WobbleSyncGroup:
# Update all member sprites using the manager reference # Update all member sprites using the manager reference
var sprites = manager_ref.get_tree().get_nodes_in_group("saved") var sprites = manager_ref.get_tree().get_nodes_in_group("saved")
print("DEBUG: Found ", sprites.size(), " total sprites in 'saved' group")
var updated_count = 0 var updated_count = 0
for sprite in sprites: for sprite in sprites:
print("DEBUG: Checking sprite ID ", sprite.id, " (type: ", typeof(sprite.id), ") against member list")
if sprite.id in memberSprites: if sprite.id in memberSprites:
print("DEBUG: Updating sprite ID ", sprite.id, " with new wobble values (no scaling - values stored as-is)")
# Preserve the sprite's group membership # Preserve the sprite's group membership
var current_group = sprite.wobbleSyncGroup var current_group = sprite.wobbleSyncGroup
sprite.xFrq = xFrq sprite.xFrq = xFrq
@@ -61,19 +54,13 @@ class WobbleSyncGroup:
# Restore group membership in case it got cleared # Restore group membership in case it got cleared
sprite.wobbleSyncGroup = current_group sprite.wobbleSyncGroup = current_group
updated_count += 1 updated_count += 1
else:
print("DEBUG: Sprite ID ", sprite.id, " not in member list")
print("DEBUG: Updated ", updated_count, " sprites in group '", name, "'")
## Add sprite to this group ## Add sprite to this group
func addSprite(sprite_id): # Remove type constraint to handle float IDs func addSprite(sprite_id): # Remove type constraint to handle float IDs
print("DEBUG: WobbleSyncGroup.addSprite called with sprite_id: ", sprite_id, " (type: ", typeof(sprite_id), ")")
if sprite_id not in memberSprites: if sprite_id not in memberSprites:
memberSprites.append(sprite_id) memberSprites.append(sprite_id)
print("DEBUG: Added sprite to memberSprites. Current members: ", memberSprites)
else: else:
print("DEBUG: Sprite already in memberSprites") pass # Sprite already in group
## Remove sprite from this group ## Remove sprite from this group
func removeSprite(sprite_id): # Remove type constraint func removeSprite(sprite_id): # Remove type constraint
@@ -144,10 +131,8 @@ func deleteGroup(group_name: String):
## Add sprite to a group ## Add sprite to a group
func addSpriteToGroup(sprite_id, group_name: String) -> bool: # Remove type constraint func addSpriteToGroup(sprite_id, group_name: String) -> bool: # Remove type constraint
print("DEBUG: addSpriteToGroup called with sprite_id: ", sprite_id, " (type: ", typeof(sprite_id), "), group: '", group_name, "'")
if not syncGroups.has(group_name): if not syncGroups.has(group_name):
print("DEBUG: Group '", group_name, "' not found")
return false return false
# Remove sprite from any existing group first # Remove sprite from any existing group first
@@ -156,12 +141,10 @@ func addSpriteToGroup(sprite_id, group_name: String) -> bool: # Remove type con
var group = syncGroups[group_name] var group = syncGroups[group_name]
group.addSprite(sprite_id) group.addSprite(sprite_id)
spriteGroupMembership[sprite_id] = group_name spriteGroupMembership[sprite_id] = group_name
print("DEBUG: Added sprite ", sprite_id, " to group '", group_name, "'. Group now has ", group.memberSprites.size(), " members")
# Sync the sprite's wobble parameters to the group # Sync the sprite's wobble parameters to the group
var sprite = getSpriteById(sprite_id) var sprite = getSpriteById(sprite_id)
if sprite: if sprite:
print("DEBUG: Found sprite object, syncing wobble parameters to group values")
sprite.wobbleSyncGroup = group_name sprite.wobbleSyncGroup = group_name
sprite.xFrq = group.xFrq sprite.xFrq = group.xFrq
sprite.xAmp = group.xAmp sprite.xAmp = group.xAmp
@@ -176,24 +159,19 @@ func addSpriteToGroup(sprite_id, group_name: String) -> bool: # Remove type con
## Remove sprite from a specific group ## Remove sprite from a specific group
func removeSpriteFromGroup(sprite_id, group_name: String): # Remove type constraint func removeSpriteFromGroup(sprite_id, group_name: String): # Remove type constraint
print("DEBUG: removeSpriteFromGroup called for sprite ID: ", sprite_id, " (type: ", typeof(sprite_id), "), group: '", group_name, "'")
if not syncGroups.has(group_name): if not syncGroups.has(group_name):
print("DEBUG: Group '", group_name, "' not found")
return return
var group = syncGroups[group_name] var group = syncGroups[group_name]
print("DEBUG: Group before removal has ", group.memberSprites.size(), " members: ", group.memberSprites)
group.removeSprite(sprite_id) group.removeSprite(sprite_id)
spriteGroupMembership.erase(sprite_id) spriteGroupMembership.erase(sprite_id)
print("DEBUG: Group after removal has ", group.memberSprites.size(), " members: ", group.memberSprites)
# Clear the sprite's sync group reference # Clear the sprite's sync group reference
var sprite = getSpriteById(sprite_id) var sprite = getSpriteById(sprite_id)
if sprite: if sprite:
print("DEBUG: Clearing sprite's wobbleSyncGroup property")
sprite.wobbleSyncGroup = "" sprite.wobbleSyncGroup = ""
else: else:
print("DEBUG: Could not find sprite object for ID: ", sprite_id) pass # Sprite not found
sprite_left_group.emit(sprite_id, group_name) sprite_left_group.emit(sprite_id, group_name)
@@ -205,27 +183,19 @@ func removeSpriteFromGroup(sprite_id, group_name: String): # Remove type constr
## Remove sprite from all groups ## Remove sprite from all groups
func removeSpriteFromAllGroups(sprite_id): # Remove type constraint func removeSpriteFromAllGroups(sprite_id): # Remove type constraint
print("DEBUG: removeSpriteFromAllGroups called for sprite ID: ", sprite_id)
if spriteGroupMembership.has(sprite_id): if spriteGroupMembership.has(sprite_id):
var group_name = spriteGroupMembership[sprite_id] var group_name = spriteGroupMembership[sprite_id]
print("DEBUG: Found sprite in group '", group_name, "', removing...")
removeSpriteFromGroup(sprite_id, group_name) removeSpriteFromGroup(sprite_id, group_name)
else: else:
print("DEBUG: Sprite not found in any group membership") pass # Sprite not in any group
## Update wobble parameters for a group ## Update wobble parameters for a group
func updateGroupWobble(group_name: String, x_frq: float, x_amp: float, y_frq: float, y_amp: float, r_frq: float, r_amp: float): func updateGroupWobble(group_name: String, x_frq: float, x_amp: float, y_frq: float, y_amp: float, r_frq: float, r_amp: float):
print("DEBUG: updateGroupWobble called for group '", group_name, "' with values - xFrq:", x_frq, " xAmp:", x_amp, " yFrq:", y_frq, " yAmp:", y_amp, " rFrq:", r_frq, " rAmp:", r_amp)
if not syncGroups.has(group_name): if not syncGroups.has(group_name):
print("DEBUG: Group '", group_name, "' not found in syncGroups!")
print("DEBUG: Available groups: ", syncGroups.keys())
print("DEBUG: This suggests the avatar's wobble sync groups were never loaded from the save file!")
print("DEBUG: Either the save file doesn't contain wobbleSyncGroups, or the loading process didn't run.")
return return
var group = syncGroups[group_name] var group = syncGroups[group_name]
print("DEBUG: Found group, calling setWobbleParameters on group with ", group.memberSprites.size(), " members")
group.setWobbleParameters(x_frq, x_amp, y_frq, y_amp, r_frq, r_amp, self) group.setWobbleParameters(x_frq, x_amp, y_frq, y_amp, r_frq, r_amp, self)
## Get sprite's current group name ## Get sprite's current group name
@@ -234,14 +204,10 @@ func getSpriteGroupName(sprite_id: int) -> String:
## Get all group names ## Get all group names
func getGroupNames() -> Array: func getGroupNames() -> Array:
print("DEBUG: getGroupNames called. syncGroups has ", syncGroups.size(), " groups: ", syncGroups.keys())
print("DEBUG: getGroupNames - syncGroups reference: ", syncGroups)
print("DEBUG: getGroupNames - self reference: ", self)
return syncGroups.keys() return syncGroups.keys()
## Test function to manually load wobble sync groups ## Test function to manually load wobble sync groups
func testManualLoad(): func testManualLoad():
print("DEBUG: testManualLoad called - creating test data...")
var test_data = { var test_data = {
"ears": { "ears": {
"name": "ears", "name": "ears",
@@ -254,9 +220,7 @@ func testManualLoad():
"memberSprites": [3291286625.0] "memberSprites": [3291286625.0]
} }
} }
print("DEBUG: Calling loadSaveData with test data...")
await loadSaveData(test_data) await loadSaveData(test_data)
print("DEBUG: testManualLoad completed. syncGroups now has: ", syncGroups.size(), " groups")
## Check if a group exists ## Check if a group exists
func hasGroup(group_name: String) -> bool: func hasGroup(group_name: String) -> bool:
@@ -305,91 +269,52 @@ func getSaveData() -> Dictionary:
## Load save data for all groups ## Load save data for all groups
func loadSaveData(save_data: Dictionary): func loadSaveData(save_data: Dictionary):
print("DEBUG: ========================")
print("DEBUG: WobbleSyncManager.loadSaveData called with save_data: ", save_data)
print("DEBUG: save_data type: ", typeof(save_data))
print("DEBUG: save_data.keys(): ", save_data.keys())
print("DEBUG: save_data.size(): ", save_data.size())
print("DEBUG: self reference: ", self)
print("DEBUG: syncGroups before clear: ", syncGroups)
# Check if save_data is valid # Check if save_data is valid
if save_data == null or typeof(save_data) != TYPE_DICTIONARY: if save_data == null or typeof(save_data) != TYPE_DICTIONARY:
print("DEBUG: ERROR - Invalid save_data provided")
return return
if save_data.size() == 0: if save_data.size() == 0:
print("DEBUG: WARNING - Empty save_data provided")
return return
# Clear existing groups # Clear existing groups
print("DEBUG: Clearing existing syncGroups (had ", syncGroups.size(), " groups)")
syncGroups.clear() syncGroups.clear()
spriteGroupMembership.clear() spriteGroupMembership.clear()
print("DEBUG: Cleared existing data. syncGroups now has ", syncGroups.size(), " groups")
# Load groups from save data # Load groups from save data
var loaded_groups = 0 var loaded_groups = 0
print("DEBUG: Starting group loading loop...")
for group_name in save_data.keys(): for group_name in save_data.keys():
print("DEBUG: ---- Processing group '", group_name, "' (attempt ", loaded_groups + 1, ") ----")
var group_data = save_data[group_name] var group_data = save_data[group_name]
print("DEBUG: Group data type: ", typeof(group_data))
print("DEBUG: Group data: ", group_data)
if typeof(group_data) == TYPE_DICTIONARY: if typeof(group_data) == TYPE_DICTIONARY:
print("DEBUG: Creating WobbleSyncGroup instance...")
var group = WobbleSyncGroup.new(group_name) var group = WobbleSyncGroup.new(group_name)
print("DEBUG: Created new WobbleSyncGroup instance for '", group_name, "'")
print("DEBUG: Calling fromSaveData...")
group.fromSaveData(group_data) group.fromSaveData(group_data)
print("DEBUG: Called fromSaveData on group '", group_name, "'")
print("DEBUG: Adding to syncGroups dictionary...")
syncGroups[group_name] = group syncGroups[group_name] = group
loaded_groups += 1 loaded_groups += 1
print("DEBUG: Added group '", group_name, "' to syncGroups. Total groups now: ", syncGroups.size())
print("DEBUG: syncGroups.keys() now: ", syncGroups.keys())
print("DEBUG: Group memberSprites: ", group.memberSprites)
# Update sprite group membership # Update sprite group membership
print("DEBUG: Updating sprite group membership...")
for sprite_id in group.memberSprites: for sprite_id in group.memberSprites:
spriteGroupMembership[sprite_id] = group_name spriteGroupMembership[sprite_id] = group_name
print("DEBUG: Added sprite ", sprite_id, " to spriteGroupMembership for group '", group_name, "'")
print("DEBUG: ---- Finished processing group '", group_name, "' ----")
else: else:
print("DEBUG: ERROR - Group data for '", group_name, "' is not a Dictionary, skipping") pass # Invalid group data
print("DEBUG: ========================")
print("DEBUG: GROUP LOADING COMPLETE")
print("DEBUG: Loaded ", loaded_groups, " groups total")
print("DEBUG: Final syncGroups after loading: ", syncGroups.keys())
print("DEBUG: Final syncGroups size: ", syncGroups.size())
print("DEBUG: Final syncGroups contents: ", syncGroups)
print("DEBUG: Final spriteGroupMembership: ", spriteGroupMembership)
print("DEBUG: ========================")
# If no groups were loaded, clear any orphaned group references on sprites # If no groups were loaded, clear any orphaned group references on sprites
if syncGroups.size() == 0: if syncGroups.size() == 0:
print("DEBUG: No groups loaded, clearing orphaned sprite group references")
var sprites = get_tree().get_nodes_in_group("saved") var sprites = get_tree().get_nodes_in_group("saved")
for sprite in sprites: for sprite in sprites:
if sprite.wobbleSyncGroup != "": if sprite.wobbleSyncGroup != "":
print("DEBUG: Clearing orphaned group reference '", sprite.wobbleSyncGroup, "' from sprite ID ", sprite.id)
sprite.wobbleSyncGroup = "" sprite.wobbleSyncGroup = ""
else: else:
print("DEBUG: Groups loaded successfully, syncing sprite parameters...")
# Apply wobble parameters to sprites with better timing # Apply wobble parameters to sprites with better timing
await get_tree().process_frame # Wait for sprites to be loaded await get_tree().process_frame # Wait for sprites to be loaded
await get_tree().process_frame # Extra frame to ensure everything is ready await get_tree().process_frame # Extra frame to ensure everything is ready
for group_name in syncGroups.keys(): for group_name in syncGroups.keys():
var group = syncGroups[group_name] var group = syncGroups[group_name]
print("DEBUG: Syncing parameters for group '", group_name, "' with ", group.memberSprites.size(), " members")
for sprite_id in group.memberSprites: for sprite_id in group.memberSprites:
var sprite = getSpriteById(sprite_id) var sprite = getSpriteById(sprite_id)
if sprite: if sprite:
print("DEBUG: Syncing sprite ID ", sprite_id, " to group '", group_name, "'")
sprite.wobbleSyncGroup = group_name sprite.wobbleSyncGroup = group_name
sprite.xFrq = group.xFrq sprite.xFrq = group.xFrq
sprite.xAmp = group.xAmp sprite.xAmp = group.xAmp
@@ -403,50 +328,9 @@ func loadSaveData(save_data: Dictionary):
if sprite.has_method("updateShaderOpacity"): if sprite.has_method("updateShaderOpacity"):
sprite.updateShaderOpacity() sprite.updateShaderOpacity()
else: else:
print("DEBUG: ERROR - Could not find sprite with ID ", sprite_id) pass # Sprite not found
print("DEBUG: ========================")
print("DEBUG: loadSaveData FUNCTION COMPLETE")
print("DEBUG: Final verification - syncGroups has ", syncGroups.size(), " groups: ", syncGroups.keys())
print("DEBUG: ========================")
## Called when a sprite is deleted to clean up references ## Called when a sprite is deleted to clean up references
func onSpriteDeleted(sprite_id): # Remove type constraint func onSpriteDeleted(sprite_id): # Remove type constraint
removeSpriteFromAllGroups(sprite_id) removeSpriteFromAllGroups(sprite_id)
## Debug function to check data consistency
func checkDataConsistency():
print("=== WOBBLE SYNC DATA CONSISTENCY CHECK ===")
# Check each group's member sprites
for group_name in syncGroups.keys():
var group = syncGroups[group_name]
print("Group '", group_name, "' has ", group.memberSprites.size(), " members: ", group.memberSprites)
for sprite_id in group.memberSprites:
# Check if sprite exists
var sprite = getSpriteById(sprite_id)
if sprite == null:
print(" ERROR: Sprite ID ", sprite_id, " in group but sprite object not found!")
else:
# Check if sprite's wobbleSyncGroup matches
if sprite.wobbleSyncGroup != group_name:
print(" ERROR: Sprite ID ", sprite_id, " in group '", group_name, "' but sprite.wobbleSyncGroup = '", sprite.wobbleSyncGroup, "'")
else:
print(" OK: Sprite ID ", sprite_id, " consistent")
# Check if membership mapping is correct
if not spriteGroupMembership.has(sprite_id) or spriteGroupMembership[sprite_id] != group_name:
print(" ERROR: Sprite ID ", sprite_id, " in group but spriteGroupMembership is wrong!")
# Check membership mapping for orphaned entries
for sprite_id in spriteGroupMembership.keys():
var group_name = spriteGroupMembership[sprite_id]
if not syncGroups.has(group_name):
print("ERROR: Sprite ID ", sprite_id, " mapped to non-existent group '", group_name, "'")
else:
var group = syncGroups[group_name]
if sprite_id not in group.memberSprites:
print("ERROR: Sprite ID ", sprite_id, " mapped to group '", group_name, "' but not in group's member list")
print("=== END CONSISTENCY CHECK ===")

View File

@@ -75,6 +75,7 @@ func _ready():
Global.main = self Global.main = self
Global.fail = $Failed Global.fail = $Failed
var newUser = Saving.settings["newUser"]
Global.connect("startSpeaking",onSpeak) Global.connect("startSpeaking",onSpeak)
@@ -83,22 +84,14 @@ func _ready():
if streamdeck_node != null: if streamdeck_node != null:
streamdeck_node.on_key_down.connect(changeCostumeStreamDeck) streamdeck_node.on_key_down.connect(changeCostumeStreamDeck)
if Saving.settings["newUser"]: if newUser:
print("DEBUG: MAIN._ready() - New user detected, loading default avatar") loadDefaultAvatar()
_on_load_dialog_file_selected("default")
Saving.settings["newUser"] = false
saveLoaded = true
else: else:
print("DEBUG: MAIN._ready() - Existing user, loading last avatar") var lastAvatar = Saving.settings.get("lastAvatar", "")
var lastAvatar = Saving.settings["lastAvatar"] if lastAvatar != null and lastAvatar != "" and typeof(lastAvatar) == TYPE_STRING and FileAccess.file_exists(lastAvatar):
print("DEBUG: lastAvatar setting: ", lastAvatar, " (type: ", typeof(lastAvatar), ")") loadAvatar(lastAvatar)
# Ensure lastAvatar is a string, not a Dictionary
if typeof(lastAvatar) == TYPE_STRING and lastAvatar != "":
print("DEBUG: Loading avatar from: ", lastAvatar)
_on_load_dialog_file_selected(lastAvatar)
else: else:
print("WARNING: lastAvatar setting is invalid, loading default instead") loadDefaultAvatar()
_on_load_dialog_file_selected("default")
$ControlPanel/volumeSlider.value = Saving.settings["volume"] $ControlPanel/volumeSlider.value = Saving.settings["volume"]
$ControlPanel/sensitiveSlider.value = Saving.settings["sense"] $ControlPanel/sensitiveSlider.value = Saving.settings["sense"]
@@ -425,54 +418,35 @@ func _on_load_button_pressed():
#LOAD AVATAR #LOAD AVATAR
func _on_load_dialog_file_selected(path): func _on_load_dialog_file_selected(path):
print("DEBUG: _on_load_dialog_file_selected called with path: ", path, " (type: ", typeof(path), ")")
# Handle case where path might be a Dictionary instead of string # Handle case where path might be a Dictionary instead of string
if typeof(path) == TYPE_DICTIONARY: if typeof(path) == TYPE_DICTIONARY:
print("ERROR: Expected string path but got Dictionary: ", path) push_error("Expected string path but got Dictionary: " + str(path))
return return
print("DEBUG: About to read save data from: ", path)
var data = Saving.read_save(path) var data = Saving.read_save(path)
print("DEBUG: Save data read. Data type: ", typeof(data), ", null check: ", data == null)
if data == null: if data == null:
print("DEBUG: Save data is null, returning")
return return
print("DEBUG: Save data keys: ", data.keys() if typeof(data) == TYPE_DICTIONARY else "not a dictionary")
print("DEBUG: Save data size: ", data.size() if typeof(data) == TYPE_DICTIONARY else "not a dictionary")
# Check the structure of the first few items
if typeof(data) == TYPE_DICTIONARY:
var count = 0
for key in data.keys():
if count < 3: # Just show first 3 items for debugging
print("DEBUG: data[", key, "] = ", data[key])
print("DEBUG: data[", key, "] type = ", typeof(data[key]))
if typeof(data[key]) == TYPE_DICTIONARY and data[key].has("path"):
print("DEBUG: data[", key, "][\"path\"] = ", data[key]["path"])
else:
print("DEBUG: data[", key, "] doesn't have 'path' key or isn't a dictionary")
count += 1
origin.queue_free() origin.queue_free()
var new = Node2D.new() var new = Node2D.new()
$OriginMotion.add_child(new) $OriginMotion.add_child(new)
origin = new origin = new
for item in data: for item in data:
print("DEBUG: Processing item: ", item, ", data type: ", typeof(data[item]))
if typeof(data[item]) != TYPE_DICTIONARY: if typeof(data[item]) != TYPE_DICTIONARY:
print("DEBUG: Skipping item ", item, " - not a dictionary")
continue continue
if not data[item].has("path"): if not data[item].has("path"):
print("DEBUG: Skipping item ", item, " - no 'path' key. Keys: ", data[item].keys())
continue continue
var sprite = spriteObject.instantiate() var sprite = spriteObject.instantiate()
sprite.path = data[item]["path"] sprite.path = data[item]["path"]
# Load display name (with backward compatibility)
if data[item].has("displayName"):
sprite.displayName = data[item]["displayName"]
sprite.id = data[item]["identification"] sprite.id = data[item]["identification"]
sprite.parentId = data[item]["parentId"] sprite.parentId = data[item]["parentId"]
@@ -503,9 +477,9 @@ func _on_load_dialog_file_selected(path):
if data[item].has("costumeLayers"): if data[item].has("costumeLayers"):
sprite.costumeLayers = str_to_var(data[item]["costumeLayers"]).duplicate() sprite.costumeLayers = str_to_var(data[item]["costumeLayers"]).duplicate()
if sprite.costumeLayers.size() < 8: # Ensure we have exactly 10 costume layers
for i in range(5): while sprite.costumeLayers.size() < 10:
sprite.costumeLayers.append(1) sprite.costumeLayers.append(1)
if data[item].has("stretchAmount"): if data[item].has("stretchAmount"):
sprite.stretchAmount = data[item]["stretchAmount"] sprite.stretchAmount = data[item]["stretchAmount"]
@@ -542,23 +516,8 @@ func _on_load_dialog_file_selected(path):
Global.spriteList.updateData() Global.spriteList.updateData()
# Load wobble sync groups # Load wobble sync groups
print("DEBUG: About to check for wobble sync groups...")
print("DEBUG: data type: ", typeof(data), ", data.keys(): ", data.keys() if typeof(data) == TYPE_DICTIONARY else "not a dictionary")
print("DEBUG: data.has('wobbleSyncGroups'): ", data.has("wobbleSyncGroups"))
print("DEBUG: WobbleSyncManager exists: ", WobbleSyncManager != null)
if WobbleSyncManager != null:
print("DEBUG: WobbleSyncManager type: ", typeof(WobbleSyncManager))
# Show save data content related to wobble sync
if data.has("wobbleSyncGroups"):
print("DEBUG: wobbleSyncGroups data found: ", data["wobbleSyncGroups"])
else:
print("DEBUG: No wobbleSyncGroups found in save data")
if data.has("wobbleSyncGroups") and WobbleSyncManager: if data.has("wobbleSyncGroups") and WobbleSyncManager:
print("DEBUG: Loading wobble sync groups. Data: ", data["wobbleSyncGroups"])
await WobbleSyncManager.loadSaveData(data["wobbleSyncGroups"]) await WobbleSyncManager.loadSaveData(data["wobbleSyncGroups"])
print("DEBUG: Finished loading wobble sync groups")
# Refresh wobble sync UI after groups are loaded # Refresh wobble sync UI after groups are loaded
if Global.spriteEdit and Global.spriteEdit.wobbleSyncControl: if Global.spriteEdit and Global.spriteEdit.wobbleSyncControl:
Global.spriteEdit.wobbleSyncControl.updateUI() Global.spriteEdit.wobbleSyncControl.updateUI()
@@ -568,14 +527,6 @@ func _on_load_dialog_file_selected(path):
# Force refresh in case timing issues # Force refresh in case timing issues
await get_tree().process_frame await get_tree().process_frame
Global.spriteEdit.wobbleSyncControl.forceRefresh() Global.spriteEdit.wobbleSyncControl.forceRefresh()
else:
print("DEBUG: Not loading wobble sync groups - missing data or manager")
# Final debug check
if WobbleSyncManager:
print("DEBUG: Final check - WobbleSyncManager has ", WobbleSyncManager.getGroupNames().size(), " groups: ", WobbleSyncManager.getGroupNames())
else:
print("DEBUG: No wobble sync groups to load. has key: ", data.has("wobbleSyncGroups"), ", WobbleSyncManager: ", WobbleSyncManager != null)
# Update opacity shaders for all loaded sprites after wobble sync data is applied # Update opacity shaders for all loaded sprites after wobble sync data is applied
await get_tree().process_frame # Wait for all sprites to be fully initialized await get_tree().process_frame # Wait for all sprites to be fully initialized
@@ -587,7 +538,15 @@ func _on_load_dialog_file_selected(path):
Global.pushUpdate("Loaded avatar at: " + path) Global.pushUpdate("Loaded avatar at: " + path)
onWindowSizeChange() onWindowSizeChange()
## Load the default avatar data
func loadDefaultAvatar():
_on_load_dialog_file_selected("default")
## Load avatar from a specific file path
func loadAvatar(path: String):
_on_load_dialog_file_selected(path)
#SAVE AVATAR #SAVE AVATAR
func _on_save_dialog_file_selected(path): func _on_save_dialog_file_selected(path):
var data = {} var data = {}
@@ -599,6 +558,7 @@ func _on_save_dialog_file_selected(path):
data[id] = {} data[id] = {}
data[id]["type"] = "sprite" data[id]["type"] = "sprite"
data[id]["path"] = child.path data[id]["path"] = child.path
data[id]["displayName"] = child.displayName
data[id]["imageData"] = Marshalls.raw_to_base64(child.imageData.save_png_to_buffer()) data[id]["imageData"] = Marshalls.raw_to_base64(child.imageData.save_png_to_buffer())
data[id]["identification"] = child.id data[id]["identification"] = child.id
data[id]["parentId"] = child.parentId data[id]["parentId"] = child.parentId
@@ -758,9 +718,11 @@ func changeCostumeStreamDeck(id: String):
"9":changeCostume(9) "9":changeCostume(9)
"10":changeCostume(10) "10":changeCostume(10)
func changeCostume(newCostume): func changeCostume(newCostume, preserve_selection: bool = false):
costume = newCostume costume = newCostume
Global.heldSprite = null # Only clear heldSprite for actual costume changes, not layer updates
if not preserve_selection:
Global.heldSprite = null
var nodes = get_tree().get_nodes_in_group("saved") var nodes = get_tree().get_nodes_in_group("saved")
for sprite in nodes: for sprite in nodes:
if sprite.costumeLayers[newCostume-1] == 1: if sprite.costumeLayers[newCostume-1] == 1:
@@ -783,7 +745,7 @@ func moveSpriteMenu(delta):
var size = get_viewport().get_visible_rect().size var size = get_viewport().get_visible_rect().size
var windowLength = 1800 var windowLength = 2000
$ViewerArrows/Arrows.position.y = size.y - 25 $ViewerArrows/Arrows.position.y = size.y - 25

View File

@@ -14357,26 +14357,38 @@ offset_bottom = -51.0
text = "Zoom : 100%" text = "Zoom : 100%"
horizontal_alignment = 2 horizontal_alignment = 2
[node name="VersionLabels" type="Node2D" parent="ControlPanel"] [node name="VersionLabels" type="Control" parent="ControlPanel"]
position = Vector2(-284, 59) layout_mode = 3
anchors_preset = 2
anchor_top = 1.0
anchor_bottom = 1.0
offset_left = -870.0
offset_top = -107.0
offset_right = -610.0
offset_bottom = -7.0
grow_vertical = 0
[node name="Label" type="Label" parent="ControlPanel/VersionLabels"] [node name="VBoxContainer" type="VBoxContainer" parent="ControlPanel/VersionLabels"]
offset_left = -713.0 layout_mode = 1
offset_top = -94.0 anchors_preset = 2
offset_right = -575.0 anchor_top = 1.0
offset_bottom = -60.0 anchor_bottom = 1.0
offset_top = -100.0
offset_right = 138.0
offset_bottom = 1.0
grow_vertical = 0
[node name="Label" type="Label" parent="ControlPanel/VersionLabels/VBoxContainer"]
layout_mode = 2
text = "PNGTuber Plus" text = "PNGTuber Plus"
label_settings = SubResource("LabelSettings_w0f1f") label_settings = SubResource("LabelSettings_w0f1f")
[node name="Label2" type="Label" parent="ControlPanel/VersionLabels"] [node name="Label2" type="Label" parent="ControlPanel/VersionLabels/VBoxContainer"]
offset_left = -713.0 layout_mode = 2
offset_top = -123.0
offset_right = -575.0
offset_bottom = -89.0
text = "kaiakairos'" text = "kaiakairos'"
label_settings = SubResource("LabelSettings_qg0do") label_settings = SubResource("LabelSettings_qg0do")
[node name="versionNo" type="Label" parent="ControlPanel/VersionLabels/Label2"] [node name="versionNo" type="Label" parent="ControlPanel/VersionLabels/VBoxContainer/Label2"]
layout_mode = 0 layout_mode = 0
offset_left = 104.0 offset_left = 104.0
offset_top = 11.0 offset_top = 11.0
@@ -14385,15 +14397,13 @@ offset_bottom = 54.0
text = "v1.4.5" text = "v1.4.5"
label_settings = SubResource("LabelSettings_xvf50") label_settings = SubResource("LabelSettings_xvf50")
[node name="Label3" type="Label" parent="ControlPanel/VersionLabels"] [node name="Label3" type="Label" parent="ControlPanel/VersionLabels/VBoxContainer"]
offset_left = -566.0 layout_mode = 2
offset_top = -93.0
offset_right = -428.0
offset_bottom = -59.0
text = "Litruv Edition" text = "Litruv Edition"
label_settings = SubResource("LabelSettings_qg0do") label_settings = SubResource("LabelSettings_qg0do")
[node name="EditControls" type="Node2D" parent="."] [node name="EditControls" type="Node2D" parent="."]
visible = false
z_index = 4090 z_index = 4090
script = ExtResource("17_bf1ic") script = ExtResource("17_bf1ic")

View File

@@ -50,11 +50,6 @@ window/vsync/vsync_mode=0
project/assembly_name="PNGTuberPlus" project/assembly_name="PNGTuberPlus"
[editor]
version_control/plugin_name=""
version_control/autoload_on_startup=false
[editor_plugins] [editor_plugins]
enabled=PackedStringArray("res://addons/godot-streamdeck-addon/plugin.cfg") enabled=PackedStringArray("res://addons/godot-streamdeck-addon/plugin.cfg")

View File

@@ -1,30 +0,0 @@
extends Node
## Test script for wobble sync groups - run this to verify the system is working
func _ready():
# Wait a frame for everything to initialize
await get_tree().process_frame
print("=== Wobble Sync Groups Test ===")
# Test creating a group
var success = WobbleSyncManager.createGroup("TestGroup")
print("Create group 'TestGroup': ", success)
# Test getting group names
var groups = WobbleSyncManager.getGroupNames()
print("Available groups: ", groups)
# Test group existence
print("Group 'TestGroup' exists: ", WobbleSyncManager.hasGroup("TestGroup"))
print("Group 'NonExistent' exists: ", WobbleSyncManager.hasGroup("NonExistent"))
# Clean up test group
WobbleSyncManager.deleteGroup("TestGroup")
print("Deleted test group")
groups = WobbleSyncManager.getGroupNames()
print("Groups after deletion: ", groups)
print("=== Test Complete ===")

View File

@@ -1 +0,0 @@
uid://ca66ai4v1k0sw

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://41u8egcau00i"
path="res://.godot/imported/checkmark.png-7bf604eba3017c173b17bad2b166951a.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://ui_scenes/button sprites/checkmark.png"
dest_files=["res://.godot/imported/checkmark.png-7bf604eba3017c173b17bad2b166951a.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View File

@@ -1,82 +0,0 @@
## Example implementation showing how to replace legacy wobble controls
## with the new WobbleControlPanel component
extends Node2D
## This demonstrates the integration approach for the sprite viewer
## Legacy slider references (to be replaced)
@onready var legacy_wobble_control = $LegacyWobbleControl
## New component system
@onready var wobble_panel: WobbleControlPanel = $WobbleControlPanel
var slider_manager: SliderManager
func _ready():
# Initialize slider management
slider_manager = SliderManager.new(self)
# Set up the wobble panel
if wobble_panel:
# The panel will automatically configure its sliders
print("WobbleControlPanel initialized")
# Hide legacy controls when using new system
if legacy_wobble_control:
legacy_wobble_control.visible = false
## Updated setImage function using new components
func setImage_new_system():
if Global.heldSprite == null:
return
print("Setting image with new component system")
# Update wobble panel with current sprite
if wobble_panel:
wobble_panel.set_target_sprite(Global.heldSprite)
wobble_panel.update_from_sprite()
# Update sync feedback
updateWobbleControlStates_new_system()
## Updated wobble control states using new system
func updateWobbleControlStates_new_system():
if Global.heldSprite == null:
return
if wobble_panel:
wobble_panel.update_sync_feedback()
## Migration helper - gradually replace sections
func migrate_to_new_system():
print("Migrating wobble controls to new component system...")
# Step 1: Hide old controls
if has_node("WobbleControl"):
$WobbleControl.visible = false
# Step 2: Show new panel
if wobble_panel:
wobble_panel.visible = true
# Step 3: Update with current sprite
if Global.heldSprite:
setImage_new_system()
print("Migration complete!")
## Demonstration of component benefits
func demonstrate_consistency():
print("=== Component System Benefits ===")
if wobble_panel:
print("All wobble sliders have consistent:")
print("- Visual styling")
print("- Value formatting")
print("- Label positioning")
print("- Signal handling")
print("- Sync state feedback")
print("Components are easily reusable across scenes")
print("Centralized management through SliderManager")
print("Type-safe with proper class inheritance")

View File

@@ -1 +0,0 @@
uid://btcluxe7swh2j

View File

@@ -1,35 +0,0 @@
[gd_scene load_steps=3 format=4 uid="uid://cm4n7yap2k8q1"]
[ext_resource type="Script" path="res://ui_scenes/components/ComponentMigrationExample.gd" id="1_migration_script"]
[ext_resource type="PackedScene" uid="uid://bp2q8r4l6k9m3" path="res://ui_scenes/components/WobbleControlPanel.tscn" id="2_wobble_panel"]
[node name="ComponentMigrationExample" type="Node2D"]
script = ExtResource("1_migration_script")
[node name="Background" type="ColorRect" parent="."]
offset_right = 300.0
offset_bottom = 400.0
color = Color(0.2, 0.2, 0.2, 1)
[node name="Title" type="Label" parent="."]
offset_left = 10.0
offset_top = 10.0
offset_right = 290.0
offset_bottom = 35.0
text = "New Component System Demo"
horizontal_alignment = 1
[node name="WobbleControlPanel" parent="." instance=ExtResource("2_wobble_panel")]
offset_left = 10.0
offset_top = 50.0
offset_right = 290.0
offset_bottom = 350.0
[node name="Instructions" type="Label" parent="."]
offset_left = 10.0
offset_top = 360.0
offset_right = 290.0
offset_bottom = 390.0
text = "These sliders automatically sync with sprites"
horizontal_alignment = 1
vertical_alignment = 1

View File

@@ -61,7 +61,6 @@ func _on_parameter_value_changed(new_value: float):
if not target_sprite or sprite_property == "": if not target_sprite or sprite_property == "":
return return
print("DEBUG: ParameterSlider ", sprite_property, " changed to: ", new_value)
target_sprite.set(sprite_property, new_value) target_sprite.set(sprite_property, new_value)

View File

@@ -45,7 +45,6 @@ func _on_wobble_value_changed(new_value: float):
if not target_sprite or parameter_name == "": if not target_sprite or parameter_name == "":
return return
print("DEBUG: WobbleSlider ", parameter_name, " changed to: ", new_value)
target_sprite.updateWobbleParameter(parameter_name, new_value) target_sprite.updateWobbleParameter(parameter_name, new_value)
# Update visual feedback for sync status # Update visual feedback for sync status

View File

@@ -6,6 +6,8 @@ var type = "sprite"
var imageData = null var imageData = null
var tex = null var tex = null
@export var path = "" @export var path = ""
## Custom display name for the sprite (separate from file path)
var displayName = ""
var loadedImageData = null var loadedImageData = null
@@ -319,8 +321,6 @@ func calculateParentOpacityMultiplier():
## Update wobble parameters and sync to group if needed ## Update wobble parameters and sync to group if needed
func updateWobbleParameter(parameter: String, value: float): func updateWobbleParameter(parameter: String, value: float):
print("DEBUG: updateWobbleParameter called - parameter: ", parameter, ", value: ", value, ", sprite ID: ", id)
print("DEBUG: Current wobbleSyncGroup: '", wobbleSyncGroup, "'")
# Update this sprite's parameter first # Update this sprite's parameter first
match parameter: match parameter:
@@ -331,17 +331,15 @@ func updateWobbleParameter(parameter: String, value: float):
"rFrq": rFrq = value "rFrq": rFrq = value
"rAmp": rAmp = value "rAmp": rAmp = value
print("DEBUG: Parameter updated on sprite. New values - xFrq:", xFrq, " xAmp:", xAmp, " yFrq:", yFrq, " yAmp:", yAmp, " rFrq:", rFrq, " rAmp:", rAmp)
# If this sprite is in a sync group, update the entire group # If this sprite is in a sync group, update the entire group
if wobbleSyncGroup != "" and WobbleSyncManager: if wobbleSyncGroup != "" and WobbleSyncManager:
print("DEBUG: Sprite is in sync group '", wobbleSyncGroup, "' - updating group wobble parameters")
WobbleSyncManager.updateGroupWobble(wobbleSyncGroup, xFrq, xAmp, yFrq, yAmp, rFrq, rAmp) WobbleSyncManager.updateGroupWobble(wobbleSyncGroup, xFrq, xAmp, yFrq, yAmp, rFrq, rAmp)
# Don't refresh UI immediately - let the sync process complete first # Don't refresh UI immediately - let the sync process complete first
# The wobble sync control will handle UI updates when needed # The wobble sync control will handle UI updates when needed
else: else:
print("DEBUG: Sprite not in sync group or WobbleSyncManager not available") pass # Individual sprite update - no sync needed
## Check if this sprite is synced to a group ## Check if this sprite is synced to a group
func isSynced() -> bool: func isSynced() -> bool:

View File

@@ -211,7 +211,6 @@ func _on_stream_deck_check_toggled(button_pressed):
func _on_experimental_mic_loudness_toggle(checked): func _on_experimental_mic_loudness_toggle(checked):
Global.experimentalMicLoudness = checked Global.experimentalMicLoudness = checked
Saving.settings["experimentalMicLoudness"] = checked Saving.settings["experimentalMicLoudness"] = checked
print("Experimental mic loudness: ", checked)
func _process(delta): func _process(delta):

View File

@@ -24,8 +24,6 @@ func _ready():
# Find the wobble sync control node # Find the wobble sync control node
wobbleSyncControl = find_child("WobbleSyncControl") wobbleSyncControl = find_child("WobbleSyncControl")
if wobbleSyncControl == null:
print("Warning: WobbleSyncControl node not found in sprite viewer")
# Set up blend mode dropdown # Set up blend mode dropdown
setupBlendModeDropdown() setupBlendModeDropdown()
@@ -355,8 +353,11 @@ func changeRotLimit():
$VBoxContainer/RotationalLimitsSection/RotBack/RotLineDisplay.rotation_degrees = Global.heldSprite.rLimitMin $VBoxContainer/RotationalLimitsSection/RotBack/RotLineDisplay.rotation_degrees = Global.heldSprite.rLimitMin
$VBoxContainer/RotationalLimitsSection/RotBack/RotLineDisplay2.rotation_degrees = Global.heldSprite.rLimitMax $VBoxContainer/RotationalLimitsSection/RotBack/RotLineDisplay2.rotation_degrees = Global.heldSprite.rLimitMax
func setLayerButtons(): func setLayerButtons():
if Global.heldSprite == null:
return
var a = Global.heldSprite.costumeLayers.duplicate() var a = Global.heldSprite.costumeLayers.duplicate()
$VBoxContainer/LayersSection/HBoxContainer/Layer1Container/Layer1.frame = 1-a[0] $VBoxContainer/LayersSection/HBoxContainer/Layer1Container/Layer1.frame = 1-a[0]
@@ -370,6 +371,10 @@ func setLayerButtons():
$VBoxContainer/LayersSection/HBoxContainer2/Layer9Container/Layer9.frame = 1-a[8] $VBoxContainer/LayersSection/HBoxContainer2/Layer9Container/Layer9.frame = 1-a[8]
$VBoxContainer/LayersSection/HBoxContainer2/Layer10Container/Layer10.frame = 1-a[9] $VBoxContainer/LayersSection/HBoxContainer2/Layer10Container/Layer10.frame = 1-a[9]
# Update checkmarks to show current costume layer
updateLayerCheckmarks()
# Update sprite visibility for all sprites based on current costume
var nodes = get_tree().get_nodes_in_group("saved") var nodes = get_tree().get_nodes_in_group("saved")
for sprite in nodes: for sprite in nodes:
if sprite.costumeLayers[Global.main.costume - 1] == 1: if sprite.costumeLayers[Global.main.costume - 1] == 1:
@@ -378,7 +383,45 @@ func setLayerButtons():
else: else:
sprite.visible = false sprite.visible = false
sprite.changeCollision(false) sprite.changeCollision(false)
# Update the sprite list to reflect visibility changes
Global.spriteList.updateAllVisible()
func updateLayerCheckmarks():
# Hide all checkmarks first
$VBoxContainer/LayersSection/HBoxContainer/Layer1Container/Layer1/Checkmark1.visible = false
$VBoxContainer/LayersSection/HBoxContainer/Layer2Container/Layer2/Checkmark2.visible = false
$VBoxContainer/LayersSection/HBoxContainer/Layer3Container/Layer3/Checkmark3.visible = false
$VBoxContainer/LayersSection/HBoxContainer/Layer4Container/Layer4/Checkmark4.visible = false
$VBoxContainer/LayersSection/HBoxContainer/Layer5Container/Layer5/Checkmark5.visible = false
$VBoxContainer/LayersSection/HBoxContainer2/Layer6Container/Layer6/Checkmark6.visible = false
$VBoxContainer/LayersSection/HBoxContainer2/Layer7Container/Layer7/Checkmark7.visible = false
$VBoxContainer/LayersSection/HBoxContainer2/Layer8Container/Layer8/Checkmark8.visible = false
$VBoxContainer/LayersSection/HBoxContainer2/Layer9Container/Layer9/Checkmark9.visible = false
$VBoxContainer/LayersSection/HBoxContainer2/Layer10Container/Layer10/Checkmark10.visible = false
# Show checkmark for current costume layer
match Global.main.costume:
1:
$VBoxContainer/LayersSection/HBoxContainer/Layer1Container/Layer1/Checkmark1.visible = true
2:
$VBoxContainer/LayersSection/HBoxContainer/Layer2Container/Layer2/Checkmark2.visible = true
3:
$VBoxContainer/LayersSection/HBoxContainer/Layer3Container/Layer3/Checkmark3.visible = true
4:
$VBoxContainer/LayersSection/HBoxContainer/Layer4Container/Layer4/Checkmark4.visible = true
5:
$VBoxContainer/LayersSection/HBoxContainer/Layer5Container/Layer5/Checkmark5.visible = true
6:
$VBoxContainer/LayersSection/HBoxContainer2/Layer6Container/Layer6/Checkmark6.visible = true
7:
$VBoxContainer/LayersSection/HBoxContainer2/Layer7Container/Layer7/Checkmark7.visible = true
8:
$VBoxContainer/LayersSection/HBoxContainer2/Layer8Container/Layer8/Checkmark8.visible = true
9:
$VBoxContainer/LayersSection/HBoxContainer2/Layer9Container/Layer9/Checkmark9.visible = true
10:
$VBoxContainer/LayersSection/HBoxContainer2/Layer10Container/Layer10/Checkmark10.visible = true
func _on_layer_button_1_pressed(): func _on_layer_button_1_pressed():
@@ -478,6 +521,11 @@ func layerSelected():
newPos = $VBoxContainer/LayersSection/HBoxContainer2/Layer9Container/Layer9.position newPos = $VBoxContainer/LayersSection/HBoxContainer2/Layer9Container/Layer9.position
10: 10:
newPos = $VBoxContainer/LayersSection/HBoxContainer2/Layer10Container/Layer10.position newPos = $VBoxContainer/LayersSection/HBoxContainer2/Layer10Container/Layer10.position
# Update selection indicator position if it exists
var select_node = get_node_or_null("VBoxContainer/LayersSection/Select")
if select_node:
select_node.position = newPos
func _on_clip_linked_toggled(button_pressed): func _on_clip_linked_toggled(button_pressed):
@@ -494,7 +542,7 @@ func _on_delete_pressed():
Global.heldSprite.makeVis() Global.heldSprite.makeVis()
func _on_set_toggle_pressed(): func _on_set_toggle_pressed():
$VBoxContainer/BoxContainer/setToggle/Labelbel.text = "toggle: AWAITING INPUT" $VBoxContainer/BoxContainer/setToggle/Label.text = "toggle: AWAITING INPUT"
await Global.main.fatfuckingballs await Global.main.fatfuckingballs
var keys = await Global.main.spriteVisToggles var keys = await Global.main.spriteVisToggles

View File

@@ -1,4 +1,4 @@
[gd_scene load_steps=77 format=4 uid="uid://d3anahesvdgfh"] [gd_scene load_steps=78 format=4 uid="uid://d3anahesvdgfh"]
[ext_resource type="Script" uid="uid://bgoaqt86bc73p" path="res://ui_scenes/spriteEditMenu/sprite_viewer.gd" id="1_hp0e3"] [ext_resource type="Script" uid="uid://bgoaqt86bc73p" path="res://ui_scenes/spriteEditMenu/sprite_viewer.gd" id="1_hp0e3"]
[ext_resource type="FontFile" uid="uid://ukj8gv8ucqsg" path="res://font/goober_pixel.ttf" id="2_da8lk"] [ext_resource type="FontFile" uid="uid://ukj8gv8ucqsg" path="res://font/goober_pixel.ttf" id="2_da8lk"]
@@ -27,11 +27,12 @@
[ext_resource type="Texture2D" uid="uid://drcl08v4k3laj" path="res://ui_scenes/spriteEditMenu/layerButtons/8.png" id="22_041y0"] [ext_resource type="Texture2D" uid="uid://drcl08v4k3laj" path="res://ui_scenes/spriteEditMenu/layerButtons/8.png" id="22_041y0"]
[ext_resource type="Texture2D" uid="uid://byyf5t56u3fq8" path="res://ui_scenes/spriteEditMenu/layerButtons/9.png" id="23_wja1u"] [ext_resource type="Texture2D" uid="uid://byyf5t56u3fq8" path="res://ui_scenes/spriteEditMenu/layerButtons/9.png" id="23_wja1u"]
[ext_resource type="Texture2D" uid="uid://dxxj6jvajo37s" path="res://ui_scenes/spriteEditMenu/layerButtons/10.png" id="24_1m26d"] [ext_resource type="Texture2D" uid="uid://dxxj6jvajo37s" path="res://ui_scenes/spriteEditMenu/layerButtons/10.png" id="24_1m26d"]
[ext_resource type="Texture2D" uid="uid://41u8egcau00i" path="res://ui_scenes/button sprites/checkmark.png" id="25_checkmark"]
[ext_resource type="Texture2D" uid="uid://23rqddatjku3" path="res://ui_scenes/mouse/tooltipBox.png" id="26_8cfad"] [ext_resource type="Texture2D" uid="uid://23rqddatjku3" path="res://ui_scenes/mouse/tooltipBox.png" id="26_8cfad"]
[ext_resource type="Texture2D" uid="uid://gflmeocxfnrq" path="res://ui_scenes/settings/deleteHotkey.png" id="27_fgu6g"] [ext_resource type="Texture2D" uid="uid://gflmeocxfnrq" path="res://ui_scenes/settings/deleteHotkey.png" id="27_fgu6g"]
[sub_resource type="RectangleShape2D" id="RectangleShape2D_21kva"] [sub_resource type="RectangleShape2D" id="RectangleShape2D_21kva"]
size = Vector2(245, 1597) size = Vector2(261, 1794)
[sub_resource type="ShaderMaterial" id="ShaderMaterial_hxmah"] [sub_resource type="ShaderMaterial" id="ShaderMaterial_hxmah"]
shader = ExtResource("3_ky0lu") shader = ExtResource("3_ky0lu")
@@ -14290,7 +14291,7 @@ script = ExtResource("1_hp0e3")
z_index = -2 z_index = -2
[node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D"] [node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D"]
position = Vector2(122.5, 798.5) position = Vector2(130.5, 899)
shape = SubResource("RectangleShape2D_21kva") shape = SubResource("RectangleShape2D_21kva")
[node name="NinePatchRect" type="NinePatchRect" parent="."] [node name="NinePatchRect" type="NinePatchRect" parent="."]
@@ -14783,7 +14784,6 @@ size_flags_horizontal = 0
size_flags_vertical = 0 size_flags_vertical = 0
[node name="Layer1" type="Sprite2D" parent="VBoxContainer/LayersSection/HBoxContainer/Layer1Container"] [node name="Layer1" type="Sprite2D" parent="VBoxContainer/LayersSection/HBoxContainer/Layer1Container"]
z_as_relative = false
position = Vector2(22, 22) position = Vector2(22, 22)
texture = ExtResource("15_scank") texture = ExtResource("15_scank")
hframes = 2 hframes = 2
@@ -14796,6 +14796,11 @@ offset_bottom = 22.0
theme = SubResource("Theme_ig6qj") theme = SubResource("Theme_ig6qj")
flat = true flat = true
[node name="Checkmark1" type="Sprite2D" parent="VBoxContainer/LayersSection/HBoxContainer/Layer1Container/Layer1"]
visible = false
position = Vector2(14, 12)
texture = ExtResource("25_checkmark")
[node name="Layer2Container" type="Control" parent="VBoxContainer/LayersSection/HBoxContainer"] [node name="Layer2Container" type="Control" parent="VBoxContainer/LayersSection/HBoxContainer"]
custom_minimum_size = Vector2(44, 44) custom_minimum_size = Vector2(44, 44)
layout_mode = 2 layout_mode = 2
@@ -14815,6 +14820,11 @@ offset_bottom = 22.0
theme = SubResource("Theme_ig6qj") theme = SubResource("Theme_ig6qj")
flat = true flat = true
[node name="Checkmark2" type="Sprite2D" parent="VBoxContainer/LayersSection/HBoxContainer/Layer2Container/Layer2"]
visible = false
position = Vector2(14, 12)
texture = ExtResource("25_checkmark")
[node name="Layer3Container" type="Control" parent="VBoxContainer/LayersSection/HBoxContainer"] [node name="Layer3Container" type="Control" parent="VBoxContainer/LayersSection/HBoxContainer"]
custom_minimum_size = Vector2(44, 44) custom_minimum_size = Vector2(44, 44)
layout_mode = 2 layout_mode = 2
@@ -14834,6 +14844,11 @@ offset_bottom = 22.0
theme = SubResource("Theme_ig6qj") theme = SubResource("Theme_ig6qj")
flat = true flat = true
[node name="Checkmark3" type="Sprite2D" parent="VBoxContainer/LayersSection/HBoxContainer/Layer3Container/Layer3"]
visible = false
position = Vector2(14, 12)
texture = ExtResource("25_checkmark")
[node name="Layer4Container" type="Control" parent="VBoxContainer/LayersSection/HBoxContainer"] [node name="Layer4Container" type="Control" parent="VBoxContainer/LayersSection/HBoxContainer"]
custom_minimum_size = Vector2(44, 44) custom_minimum_size = Vector2(44, 44)
layout_mode = 2 layout_mode = 2
@@ -14853,6 +14868,11 @@ offset_bottom = 22.0
theme = SubResource("Theme_ig6qj") theme = SubResource("Theme_ig6qj")
flat = true flat = true
[node name="Checkmark4" type="Sprite2D" parent="VBoxContainer/LayersSection/HBoxContainer/Layer4Container/Layer4"]
visible = false
position = Vector2(14, 12)
texture = ExtResource("25_checkmark")
[node name="Layer5Container" type="Control" parent="VBoxContainer/LayersSection/HBoxContainer"] [node name="Layer5Container" type="Control" parent="VBoxContainer/LayersSection/HBoxContainer"]
custom_minimum_size = Vector2(44, 44) custom_minimum_size = Vector2(44, 44)
layout_mode = 2 layout_mode = 2
@@ -14872,6 +14892,11 @@ offset_bottom = 22.0
theme = SubResource("Theme_ig6qj") theme = SubResource("Theme_ig6qj")
flat = true flat = true
[node name="Checkmark5" type="Sprite2D" parent="VBoxContainer/LayersSection/HBoxContainer/Layer5Container/Layer5"]
visible = false
position = Vector2(14, 12)
texture = ExtResource("25_checkmark")
[node name="HBoxContainer2" type="HBoxContainer" parent="VBoxContainer/LayersSection"] [node name="HBoxContainer2" type="HBoxContainer" parent="VBoxContainer/LayersSection"]
layout_mode = 2 layout_mode = 2
theme_override_constants/separation = 5 theme_override_constants/separation = 5
@@ -14895,6 +14920,11 @@ offset_bottom = 22.0
theme = SubResource("Theme_ig6qj") theme = SubResource("Theme_ig6qj")
flat = true flat = true
[node name="Checkmark6" type="Sprite2D" parent="VBoxContainer/LayersSection/HBoxContainer2/Layer6Container/Layer6"]
visible = false
position = Vector2(14, 12)
texture = ExtResource("25_checkmark")
[node name="Layer7Container" type="Control" parent="VBoxContainer/LayersSection/HBoxContainer2"] [node name="Layer7Container" type="Control" parent="VBoxContainer/LayersSection/HBoxContainer2"]
custom_minimum_size = Vector2(44, 44) custom_minimum_size = Vector2(44, 44)
layout_mode = 2 layout_mode = 2
@@ -14914,6 +14944,11 @@ offset_bottom = 22.0
theme = SubResource("Theme_ig6qj") theme = SubResource("Theme_ig6qj")
flat = true flat = true
[node name="Checkmark7" type="Sprite2D" parent="VBoxContainer/LayersSection/HBoxContainer2/Layer7Container/Layer7"]
visible = false
position = Vector2(14, 12)
texture = ExtResource("25_checkmark")
[node name="Layer8Container" type="Control" parent="VBoxContainer/LayersSection/HBoxContainer2"] [node name="Layer8Container" type="Control" parent="VBoxContainer/LayersSection/HBoxContainer2"]
custom_minimum_size = Vector2(44, 44) custom_minimum_size = Vector2(44, 44)
layout_mode = 2 layout_mode = 2
@@ -14933,6 +14968,11 @@ offset_bottom = 22.0
theme = SubResource("Theme_ig6qj") theme = SubResource("Theme_ig6qj")
flat = true flat = true
[node name="Checkmark8" type="Sprite2D" parent="VBoxContainer/LayersSection/HBoxContainer2/Layer8Container/Layer8"]
visible = false
position = Vector2(14, 12)
texture = ExtResource("25_checkmark")
[node name="Layer9Container" type="Control" parent="VBoxContainer/LayersSection/HBoxContainer2"] [node name="Layer9Container" type="Control" parent="VBoxContainer/LayersSection/HBoxContainer2"]
custom_minimum_size = Vector2(44, 44) custom_minimum_size = Vector2(44, 44)
layout_mode = 2 layout_mode = 2
@@ -14952,6 +14992,11 @@ offset_bottom = 22.0
theme = SubResource("Theme_ig6qj") theme = SubResource("Theme_ig6qj")
flat = true flat = true
[node name="Checkmark9" type="Sprite2D" parent="VBoxContainer/LayersSection/HBoxContainer2/Layer9Container/Layer9"]
visible = false
position = Vector2(14, 12)
texture = ExtResource("25_checkmark")
[node name="Layer10Container" type="Control" parent="VBoxContainer/LayersSection/HBoxContainer2"] [node name="Layer10Container" type="Control" parent="VBoxContainer/LayersSection/HBoxContainer2"]
custom_minimum_size = Vector2(44, 44) custom_minimum_size = Vector2(44, 44)
layout_mode = 2 layout_mode = 2
@@ -14971,6 +15016,11 @@ offset_bottom = 22.0
theme = SubResource("Theme_ig6qj") theme = SubResource("Theme_ig6qj")
flat = true flat = true
[node name="Checkmark10" type="Sprite2D" parent="VBoxContainer/LayersSection/HBoxContainer2/Layer10Container/Layer10"]
visible = false
position = Vector2(14, 12)
texture = ExtResource("25_checkmark")
[node name="BoxContainer" type="BoxContainer" parent="VBoxContainer"] [node name="BoxContainer" type="BoxContainer" parent="VBoxContainer"]
layout_mode = 2 layout_mode = 2
@@ -15024,5 +15074,15 @@ centered = false
[connection signal="pressed" from="VBoxContainer/Buttons/TrashWrapper/Trash/trash" to="." method="_on_trash_pressed"] [connection signal="pressed" from="VBoxContainer/Buttons/TrashWrapper/Trash/trash" to="." method="_on_trash_pressed"]
[connection signal="toggled" from="VBoxContainer/RenderingSection/ClipLinked" to="." method="_on_clip_linked_toggled"] [connection signal="toggled" from="VBoxContainer/RenderingSection/ClipLinked" to="." method="_on_clip_linked_toggled"]
[connection signal="toggled" from="VBoxContainer/PhysicsSection/BounceVelocity" to="." method="_on_check_box_toggled"] [connection signal="toggled" from="VBoxContainer/PhysicsSection/BounceVelocity" to="." method="_on_check_box_toggled"]
[connection signal="pressed" from="VBoxContainer/LayersSection/HBoxContainer/Layer1Container/Layer1/layerButton1" to="." method="_on_layer_button_1_pressed"]
[connection signal="pressed" from="VBoxContainer/LayersSection/HBoxContainer/Layer2Container/Layer2/layerButton2" to="." method="_on_layer_button_2_pressed"]
[connection signal="pressed" from="VBoxContainer/LayersSection/HBoxContainer/Layer3Container/Layer3/layerButton3" to="." method="_on_layer_button_3_pressed"]
[connection signal="pressed" from="VBoxContainer/LayersSection/HBoxContainer/Layer4Container/Layer4/layerButton4" to="." method="_on_layer_button_4_pressed"]
[connection signal="pressed" from="VBoxContainer/LayersSection/HBoxContainer/Layer5Container/Layer5/layerButton5" to="." method="_on_layer_button_5_pressed"]
[connection signal="pressed" from="VBoxContainer/LayersSection/HBoxContainer2/Layer6Container/Layer6/layerButton6" to="." method="_on_layer_button_6_pressed"]
[connection signal="pressed" from="VBoxContainer/LayersSection/HBoxContainer2/Layer7Container/Layer7/layerButton7" to="." method="_on_layer_button_7_pressed"]
[connection signal="pressed" from="VBoxContainer/LayersSection/HBoxContainer2/Layer8Container/Layer8/layerButton8" to="." method="_on_layer_button_8_pressed"]
[connection signal="pressed" from="VBoxContainer/LayersSection/HBoxContainer2/Layer9Container/Layer9/layerButton9" to="." method="_on_layer_button_9_pressed"]
[connection signal="pressed" from="VBoxContainer/LayersSection/HBoxContainer2/Layer10Container/Layer10/layerButton10" to="." method="_on_layer_button_10_pressed"]
[connection signal="pressed" from="VBoxContainer/BoxContainer/setToggle" to="." method="_on_set_toggle_pressed"] [connection signal="pressed" from="VBoxContainer/BoxContainer/setToggle" to="." method="_on_set_toggle_pressed"]
[connection signal="pressed" from="VBoxContainer/BoxContainer/setToggle/delete" to="." method="_on_delete_pressed"] [connection signal="pressed" from="VBoxContainer/BoxContainer/setToggle/delete" to="." method="_on_delete_pressed"]

View File

@@ -55,11 +55,10 @@ func updateGroupDropdown():
# Add existing groups # Add existing groups
if WobbleSyncManager: if WobbleSyncManager:
var groups = WobbleSyncManager.getGroupNames() var groups = WobbleSyncManager.getGroupNames()
print("DEBUG: updateGroupDropdown - available groups: ", groups)
for group_name in groups: for group_name in groups:
groupDropdown.add_item(group_name) groupDropdown.add_item(group_name)
else: else:
print("DEBUG: updateGroupDropdown - WobbleSyncManager not available") pass # WobbleSyncManager not available
# Add "Create New..." option # Add "Create New..." option
groupDropdown.add_separator() groupDropdown.add_separator()
@@ -67,7 +66,6 @@ func updateGroupDropdown():
# Select current group if sprite is synced # Select current group if sprite is synced
if currentSprite != null and currentSprite.wobbleSyncGroup != "": if currentSprite != null and currentSprite.wobbleSyncGroup != "":
print("DEBUG: updateGroupDropdown - sprite has group '", currentSprite.wobbleSyncGroup, "'")
var group_index = -1 var group_index = -1
for i in range(groupDropdown.get_item_count()): for i in range(groupDropdown.get_item_count()):
if groupDropdown.get_item_text(i) == currentSprite.wobbleSyncGroup: if groupDropdown.get_item_text(i) == currentSprite.wobbleSyncGroup:
@@ -75,13 +73,10 @@ func updateGroupDropdown():
break break
if group_index != -1: if group_index != -1:
print("DEBUG: updateGroupDropdown - selecting group at index ", group_index)
groupDropdown.selected = group_index groupDropdown.selected = group_index
else: else:
print("DEBUG: updateGroupDropdown - group '", currentSprite.wobbleSyncGroup, "' not found in dropdown")
groupDropdown.selected = 0 # Select "None" groupDropdown.selected = 0 # Select "None"
else: else:
print("DEBUG: updateGroupDropdown - sprite has no group, selecting 'None'")
groupDropdown.selected = 0 # Select "None" groupDropdown.selected = 0 # Select "None"
updating_dropdown = false updating_dropdown = false
@@ -100,11 +95,9 @@ func updateSyncIndicator():
## Set the current sprite to manage ## Set the current sprite to manage
func setSprite(sprite): func setSprite(sprite):
print("DEBUG: WobbleSyncControl.setSprite called with sprite ID: ", sprite.id if sprite else "null")
currentSprite = sprite currentSprite = sprite
if currentSprite: if currentSprite:
print("DEBUG: Current sprite wobbleSyncGroup: '", currentSprite.wobbleSyncGroup, "'") pass # Sprite is valid
print("DEBUG: Current sprite isSynced(): ", currentSprite.isSynced())
updateUI() updateUI()
## Get available groups for the dropdown (excluding "None" and "Create New...") ## Get available groups for the dropdown (excluding "None" and "Create New...")
@@ -120,18 +113,15 @@ func _on_group_dropdown_selected(index: int):
return return
var selected_text = groupDropdown.get_item_text(index) var selected_text = groupDropdown.get_item_text(index)
print("DEBUG: Dropdown selected: '", selected_text, "' at index ", index)
match selected_text: match selected_text:
"None": "None":
# Remove sprite from any group # Remove sprite from any group
if currentSprite.wobbleSyncGroup != "" and WobbleSyncManager: if currentSprite.wobbleSyncGroup != "" and WobbleSyncManager:
print("DEBUG: Removing sprite ID ", currentSprite.id, " from group '", currentSprite.wobbleSyncGroup, "'")
WobbleSyncManager.removeSpriteFromAllGroups(currentSprite.id) WobbleSyncManager.removeSpriteFromAllGroups(currentSprite.id)
Global.pushUpdate("Removed sprite from wobble sync group") Global.pushUpdate("Removed sprite from wobble sync group")
"Create New...": "Create New...":
print("DEBUG: User selected 'Create New...' from dropdown")
# Show create group dialog # Show create group dialog
groupNameInput.text = "" groupNameInput.text = ""
groupNameInput.placeholder_text = "Enter group name..." groupNameInput.placeholder_text = "Enter group name..."
@@ -143,45 +133,39 @@ func _on_group_dropdown_selected(index: int):
_: _:
# Join selected group # Join selected group
if WobbleSyncManager and WobbleSyncManager.hasGroup(selected_text): if WobbleSyncManager and WobbleSyncManager.hasGroup(selected_text):
print("DEBUG: Adding sprite ID ", currentSprite.id, " to group '", selected_text, "'")
WobbleSyncManager.addSpriteToGroup(currentSprite.id, selected_text) WobbleSyncManager.addSpriteToGroup(currentSprite.id, selected_text)
# Refresh sprite editor to show updated wobble values # Refresh sprite editor to show updated wobble values
if Global.spriteEdit: if Global.spriteEdit:
Global.spriteEdit.setImage() Global.spriteEdit.setImage()
else: else:
print("DEBUG: Group '", selected_text, "' not found or WobbleSyncManager not available") pass # Group doesn't exist
updateSyncIndicator() updateSyncIndicator()
## Handle create group dialog confirmation ## Handle create group dialog confirmation
func _on_create_group_confirmed(): func _on_create_group_confirmed():
var group_name = groupNameInput.text.strip_edges() var group_name = groupNameInput.text.strip_edges()
print("DEBUG: Create group confirmed with name: '", group_name, "'")
if group_name == "": if group_name == "":
print("DEBUG: Group name is empty")
Global.pushUpdate("Group name cannot be empty") Global.pushUpdate("Group name cannot be empty")
return return
if WobbleSyncManager and WobbleSyncManager.hasGroup(group_name): if WobbleSyncManager and WobbleSyncManager.hasGroup(group_name):
print("DEBUG: Group '", group_name, "' already exists")
Global.pushUpdate("Group \"" + group_name + "\" already exists") Global.pushUpdate("Group \"" + group_name + "\" already exists")
return return
# Create group with current sprite # Create group with current sprite
if currentSprite != null and WobbleSyncManager: if currentSprite != null and WobbleSyncManager:
print("DEBUG: Creating group '", group_name, "' with sprite ID ", currentSprite.id)
if WobbleSyncManager.createGroup(group_name, currentSprite.id): if WobbleSyncManager.createGroup(group_name, currentSprite.id):
print("DEBUG: Group created successfully")
createGroupDialog.hide() createGroupDialog.hide()
updateUI() updateUI()
# Refresh sprite editor to show sync status # Refresh sprite editor to show sync status
if Global.spriteEdit: if Global.spriteEdit:
Global.spriteEdit.setImage() Global.spriteEdit.setImage()
else: else:
print("DEBUG: Failed to create group") pass # Failed to create group
else: else:
print("DEBUG: No current sprite to add to group") pass # No sprite or WobbleSyncManager not available
## Handle create group dialog cancellation ## Handle create group dialog cancellation
func _on_create_group_cancelled(): func _on_create_group_cancelled():
@@ -203,5 +187,4 @@ func canSync() -> bool:
## Force refresh the UI - useful after loading saves ## Force refresh the UI - useful after loading saves
func forceRefresh(): func forceRefresh():
print("DEBUG: WobbleSyncControl.forceRefresh called")
updateUI() updateUI()

View File

@@ -2,9 +2,9 @@ extends NinePatchRect
@onready var spritePreview = $SpritePreview/Sprite2D @onready var spritePreview = $SpritePreview/Sprite2D
@onready var outline = $Selected @onready var outline = $Selected
@onready var fade = $Fade @onready var fade = $Fade
@onready var button = $Button
@onready var label = $Label
var sprite = null var sprite = null
var parent = null var parent = null
@@ -14,9 +14,23 @@ var indent = 0
var childrenTags = [] var childrenTags = []
var parentTag = null var parentTag = null
## Context menu for right-click actions
var context_menu: PopupMenu = null
## Line edit for renaming
var rename_edit: LineEdit = null
## Track if we're currently in rename mode
var is_renaming: bool = false
func _ready(): func _ready():
var count = spritePath.get_slice_count("/") - 1 # Use displayName if available, otherwise fallback to path
$Label.text = spritePath.get_slice("/",count) var display_text = ""
if sprite.displayName != "":
display_text = sprite.displayName
else:
var count = spritePath.get_slice_count("/") - 1
display_text = spritePath.get_slice("/",count)
label.text = display_text
$Line2D.visible = false $Line2D.visible = false
spritePreview.texture = sprite.sprite.texture spritePreview.texture = sprite.sprite.texture
@@ -25,6 +39,115 @@ func _ready():
spritePreview.scale = Vector2(1,1) * (60.0/displaySize) spritePreview.scale = Vector2(1,1) * (60.0/displaySize)
spritePreview.offset = sprite.sprite.offset spritePreview.offset = sprite.sprite.offset
# Set up right-click detection
button.gui_input.connect(_on_button_gui_input)
# Create context menu
_setup_context_menu()
## Set up the right-click context menu
func _setup_context_menu():
context_menu = PopupMenu.new()
add_child(context_menu)
# Add rename option
context_menu.add_item("Rename", 0)
context_menu.id_pressed.connect(_on_context_menu_selected)
# Wait for menu to be ready, then adjust size
context_menu.ready.connect(_adjust_menu_size)
## Adjust the context menu size to be more compact
func _adjust_menu_size():
if context_menu:
# Let the menu calculate its natural size first
await get_tree().process_frame
context_menu.reset_size() # Reset to content-based size
## Handle GUI input events on the button
func _on_button_gui_input(event: InputEvent):
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_RIGHT and event.pressed:
# Show context menu at cursor position
var global_pos = get_global_mouse_position()
context_menu.position = Vector2i(global_pos)
context_menu.popup()
## Handle context menu selections
func _on_context_menu_selected(id: int):
match id:
0: # Rename
_start_rename()
## Start the rename process
func _start_rename():
if is_renaming:
return
is_renaming = true
# Create line edit for renaming
rename_edit = LineEdit.new()
add_child(rename_edit)
# Position and size the line edit over the label
rename_edit.position = label.position
rename_edit.size = label.size
rename_edit.text = label.text
# Hide the original label
label.visible = false
# Focus the line edit and select all text
rename_edit.grab_focus()
rename_edit.select_all()
# Connect signals
rename_edit.text_submitted.connect(_on_rename_submitted)
rename_edit.focus_exited.connect(_on_rename_focus_lost)
## Handle rename submission (Enter key)
func _on_rename_submitted(new_name: String):
if rename_edit != null:
_finish_rename(new_name)
## Handle losing focus (clicking elsewhere)
func _on_rename_focus_lost():
if rename_edit != null:
_finish_rename(rename_edit.text)
## Finish the rename process
func _finish_rename(new_name: String):
if not is_renaming:
return
is_renaming = false
# Clean up the line edit
if rename_edit:
rename_edit.queue_free()
rename_edit = null
# Show the label again
label.visible = true
# Validate and apply the new name
new_name = new_name.strip_edges()
if new_name != "" and new_name != label.text:
_apply_rename(new_name)
## Apply the rename to the sprite
func _apply_rename(new_name: String):
# Update the display name (without file extension)
label.text = new_name
sprite.displayName = new_name # Store display name separately
# Update the global sprite list
Global.spriteList.updateData()
# Show feedback to user
Global.pushUpdate("Renamed sprite to: " + new_name)
func updateChildren(): func updateChildren():
for child in childrenTags: for child in childrenTags: