mirror of
https://github.com/litruv/PNGTuber-Plus.git
synced 2026-07-24 10:36:01 +10:00
Compare commits
7 Commits
v2025.07.2
...
v2025.07.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 95476cfd57 | |||
| 722fed9b94 | |||
| e8620611df | |||
| 85a208a729 | |||
| 92fc5dda89 | |||
| af17a1dee0 | |||
| 43dfc410dc |
@@ -15,6 +15,14 @@ Added a proper opacity slider for sprites, with an "affect children" toggle. Cli
|
||||

|
||||
|
||||
|
||||
### 🎨 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.
|
||||
|
||||
* Dropdown selector in the sprite viewer menu
|
||||
* Works alongside opacity controls
|
||||
|
||||
|
||||
### 🌀 Rotation Wobble
|
||||
|
||||
Sprites can now do a subtle (or chaotic) rotational wobble. Frequency + amplitude sliders included.
|
||||
|
||||
@@ -4,4 +4,4 @@ name="Godot Git Plugin"
|
||||
description="This plugin lets you interact with Git without leaving the Godot editor. More information can be found at https://github.com/godotengine/godot-git-plugin/wiki"
|
||||
author="twaritwaikar"
|
||||
version="v3.1.0"
|
||||
script="godot-git-plugin.gd"
|
||||
script=""
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
58
autoload/blend_mode_manager.gd
Normal file
58
autoload/blend_mode_manager.gd
Normal file
@@ -0,0 +1,58 @@
|
||||
extends Node
|
||||
|
||||
## Blend Mode Manager
|
||||
##
|
||||
## Manages blend mode constants and provides utility functions for
|
||||
## working with blend modes in the PNGTuber application.
|
||||
|
||||
## Blend mode constants
|
||||
enum BlendMode {
|
||||
NORMAL = 0,
|
||||
MULTIPLY = 1,
|
||||
SCREEN = 2,
|
||||
OVERLAY = 3,
|
||||
SOFT_LIGHT = 4,
|
||||
HARD_LIGHT = 5,
|
||||
COLOR_DODGE = 6,
|
||||
COLOR_BURN = 7,
|
||||
DARKEN = 8,
|
||||
LIGHTEN = 9,
|
||||
ADD = 10, # Linear Dodge
|
||||
SUBTRACT = 11
|
||||
}
|
||||
|
||||
## Returns an array of blend mode names for use in dropdowns
|
||||
static func get_blend_mode_names() -> Array:
|
||||
return [
|
||||
"Normal",
|
||||
"Multiply",
|
||||
"Screen",
|
||||
"Overlay",
|
||||
"Soft Light",
|
||||
"Hard Light",
|
||||
"Color Dodge",
|
||||
"Color Burn",
|
||||
"Darken",
|
||||
"Lighten",
|
||||
"Add",
|
||||
"Subtract"
|
||||
]
|
||||
|
||||
## Returns the string name for a given blend mode enum value
|
||||
static func get_blend_mode_name(mode: BlendMode) -> String:
|
||||
var names = get_blend_mode_names()
|
||||
if mode >= 0 and mode < names.size():
|
||||
return names[mode]
|
||||
return "Normal"
|
||||
|
||||
## Returns the blend mode enum value for a given name
|
||||
static func get_blend_mode_from_name(name: String) -> BlendMode:
|
||||
var names = get_blend_mode_names()
|
||||
var index = names.find(name)
|
||||
if index != -1:
|
||||
return index as BlendMode
|
||||
return BlendMode.NORMAL
|
||||
|
||||
## Returns whether a blend mode is valid
|
||||
static func is_valid_blend_mode(mode: int) -> bool:
|
||||
return mode >= BlendMode.NORMAL and mode <= BlendMode.SUBTRACT
|
||||
1
autoload/blend_mode_manager.gd.uid
Normal file
1
autoload/blend_mode_manager.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bm1aeoarbpp6b
|
||||
@@ -280,7 +280,6 @@ func blinking():
|
||||
blinkTick = -12
|
||||
|
||||
func epicFail(err):
|
||||
print(fail)
|
||||
if fail == null:
|
||||
return
|
||||
|
||||
|
||||
@@ -12,10 +12,8 @@ signal sprite_joined_group(sprite_id: int, group_name: String)
|
||||
signal sprite_left_group(sprite_id: int, group_name: String)
|
||||
|
||||
func _ready():
|
||||
print("DEBUG: WobbleSyncManager._ready() called")
|
||||
print("DEBUG: WobbleSyncManager autoload initialized successfully")
|
||||
print("DEBUG: Initial syncGroups: ", syncGroups)
|
||||
print("DEBUG: Initial spriteGroupMembership: ", spriteGroupMembership)
|
||||
# Manager initialization complete
|
||||
pass
|
||||
|
||||
class WobbleSyncGroup:
|
||||
var name: String
|
||||
@@ -32,8 +30,6 @@ class WobbleSyncGroup:
|
||||
|
||||
## 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):
|
||||
print("DEBUG: WobbleSyncGroup.setWobbleParameters called for group '", name, "' with ", memberSprites.size(), " members")
|
||||
print("DEBUG: Member sprite IDs: ", memberSprites)
|
||||
xFrq = x_frq
|
||||
xAmp = x_amp
|
||||
yFrq = y_frq
|
||||
@@ -43,13 +39,10 @@ class WobbleSyncGroup:
|
||||
|
||||
# Update all member sprites using the manager reference
|
||||
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
|
||||
for sprite in sprites:
|
||||
print("DEBUG: Checking sprite ID ", sprite.id, " (type: ", typeof(sprite.id), ") against member list")
|
||||
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
|
||||
var current_group = sprite.wobbleSyncGroup
|
||||
sprite.xFrq = xFrq
|
||||
@@ -61,19 +54,13 @@ class WobbleSyncGroup:
|
||||
# Restore group membership in case it got cleared
|
||||
sprite.wobbleSyncGroup = current_group
|
||||
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
|
||||
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:
|
||||
memberSprites.append(sprite_id)
|
||||
print("DEBUG: Added sprite to memberSprites. Current members: ", memberSprites)
|
||||
else:
|
||||
print("DEBUG: Sprite already in memberSprites")
|
||||
pass # Sprite already in group
|
||||
|
||||
## Remove sprite from this group
|
||||
func removeSprite(sprite_id): # Remove type constraint
|
||||
@@ -144,10 +131,8 @@ func deleteGroup(group_name: String):
|
||||
|
||||
## Add sprite to a group
|
||||
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):
|
||||
print("DEBUG: Group '", group_name, "' not found")
|
||||
return false
|
||||
|
||||
# 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]
|
||||
group.addSprite(sprite_id)
|
||||
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
|
||||
var sprite = getSpriteById(sprite_id)
|
||||
if sprite:
|
||||
print("DEBUG: Found sprite object, syncing wobble parameters to group values")
|
||||
sprite.wobbleSyncGroup = group_name
|
||||
sprite.xFrq = group.xFrq
|
||||
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
|
||||
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):
|
||||
print("DEBUG: Group '", group_name, "' not found")
|
||||
return
|
||||
|
||||
var group = syncGroups[group_name]
|
||||
print("DEBUG: Group before removal has ", group.memberSprites.size(), " members: ", group.memberSprites)
|
||||
group.removeSprite(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
|
||||
var sprite = getSpriteById(sprite_id)
|
||||
if sprite:
|
||||
print("DEBUG: Clearing sprite's wobbleSyncGroup property")
|
||||
sprite.wobbleSyncGroup = ""
|
||||
else:
|
||||
print("DEBUG: Could not find sprite object for ID: ", sprite_id)
|
||||
pass # Sprite not found
|
||||
|
||||
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
|
||||
func removeSpriteFromAllGroups(sprite_id): # Remove type constraint
|
||||
print("DEBUG: removeSpriteFromAllGroups called for sprite ID: ", sprite_id)
|
||||
if spriteGroupMembership.has(sprite_id):
|
||||
var group_name = spriteGroupMembership[sprite_id]
|
||||
print("DEBUG: Found sprite in group '", group_name, "', removing...")
|
||||
removeSpriteFromGroup(sprite_id, group_name)
|
||||
else:
|
||||
print("DEBUG: Sprite not found in any group membership")
|
||||
pass # Sprite not in any 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):
|
||||
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):
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
## Get sprite's current group name
|
||||
@@ -234,14 +204,10 @@ func getSpriteGroupName(sprite_id: int) -> String:
|
||||
|
||||
## Get all group names
|
||||
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()
|
||||
|
||||
## Test function to manually load wobble sync groups
|
||||
func testManualLoad():
|
||||
print("DEBUG: testManualLoad called - creating test data...")
|
||||
var test_data = {
|
||||
"ears": {
|
||||
"name": "ears",
|
||||
@@ -254,9 +220,7 @@ func testManualLoad():
|
||||
"memberSprites": [3291286625.0]
|
||||
}
|
||||
}
|
||||
print("DEBUG: Calling loadSaveData with test data...")
|
||||
await loadSaveData(test_data)
|
||||
print("DEBUG: testManualLoad completed. syncGroups now has: ", syncGroups.size(), " groups")
|
||||
|
||||
## Check if a group exists
|
||||
func hasGroup(group_name: String) -> bool:
|
||||
@@ -305,91 +269,52 @@ func getSaveData() -> Dictionary:
|
||||
|
||||
## Load save data for all groups
|
||||
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
|
||||
if save_data == null or typeof(save_data) != TYPE_DICTIONARY:
|
||||
print("DEBUG: ERROR - Invalid save_data provided")
|
||||
return
|
||||
|
||||
if save_data.size() == 0:
|
||||
print("DEBUG: WARNING - Empty save_data provided")
|
||||
return
|
||||
|
||||
# Clear existing groups
|
||||
print("DEBUG: Clearing existing syncGroups (had ", syncGroups.size(), " groups)")
|
||||
syncGroups.clear()
|
||||
spriteGroupMembership.clear()
|
||||
print("DEBUG: Cleared existing data. syncGroups now has ", syncGroups.size(), " groups")
|
||||
|
||||
# Load groups from save data
|
||||
var loaded_groups = 0
|
||||
print("DEBUG: Starting group loading loop...")
|
||||
for group_name in save_data.keys():
|
||||
print("DEBUG: ---- Processing group '", group_name, "' (attempt ", loaded_groups + 1, ") ----")
|
||||
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:
|
||||
print("DEBUG: Creating WobbleSyncGroup instance...")
|
||||
var group = WobbleSyncGroup.new(group_name)
|
||||
print("DEBUG: Created new WobbleSyncGroup instance for '", group_name, "'")
|
||||
print("DEBUG: Calling fromSaveData...")
|
||||
group.fromSaveData(group_data)
|
||||
print("DEBUG: Called fromSaveData on group '", group_name, "'")
|
||||
print("DEBUG: Adding to syncGroups dictionary...")
|
||||
syncGroups[group_name] = group
|
||||
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
|
||||
print("DEBUG: Updating sprite group membership...")
|
||||
for sprite_id in group.memberSprites:
|
||||
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:
|
||||
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 syncGroups.size() == 0:
|
||||
print("DEBUG: No groups loaded, clearing orphaned sprite group references")
|
||||
var sprites = get_tree().get_nodes_in_group("saved")
|
||||
for sprite in sprites:
|
||||
if sprite.wobbleSyncGroup != "":
|
||||
print("DEBUG: Clearing orphaned group reference '", sprite.wobbleSyncGroup, "' from sprite ID ", sprite.id)
|
||||
sprite.wobbleSyncGroup = ""
|
||||
else:
|
||||
print("DEBUG: Groups loaded successfully, syncing sprite parameters...")
|
||||
# Apply wobble parameters to sprites with better timing
|
||||
await get_tree().process_frame # Wait for sprites to be loaded
|
||||
await get_tree().process_frame # Extra frame to ensure everything is ready
|
||||
|
||||
for group_name in syncGroups.keys():
|
||||
var group = syncGroups[group_name]
|
||||
print("DEBUG: Syncing parameters for group '", group_name, "' with ", group.memberSprites.size(), " members")
|
||||
for sprite_id in group.memberSprites:
|
||||
var sprite = getSpriteById(sprite_id)
|
||||
if sprite:
|
||||
print("DEBUG: Syncing sprite ID ", sprite_id, " to group '", group_name, "'")
|
||||
sprite.wobbleSyncGroup = group_name
|
||||
sprite.xFrq = group.xFrq
|
||||
sprite.xAmp = group.xAmp
|
||||
@@ -403,50 +328,9 @@ func loadSaveData(save_data: Dictionary):
|
||||
if sprite.has_method("updateShaderOpacity"):
|
||||
sprite.updateShaderOpacity()
|
||||
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
|
||||
func onSpriteDeleted(sprite_id): # Remove type constraint
|
||||
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 ===")
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -75,6 +75,7 @@ func _ready():
|
||||
Global.main = self
|
||||
Global.fail = $Failed
|
||||
|
||||
var newUser = Saving.settings["newUser"]
|
||||
|
||||
Global.connect("startSpeaking",onSpeak)
|
||||
|
||||
@@ -83,22 +84,14 @@ func _ready():
|
||||
if streamdeck_node != null:
|
||||
streamdeck_node.on_key_down.connect(changeCostumeStreamDeck)
|
||||
|
||||
if Saving.settings["newUser"]:
|
||||
print("DEBUG: MAIN._ready() - New user detected, loading default avatar")
|
||||
_on_load_dialog_file_selected("default")
|
||||
Saving.settings["newUser"] = false
|
||||
saveLoaded = true
|
||||
if newUser:
|
||||
loadDefaultAvatar()
|
||||
else:
|
||||
print("DEBUG: MAIN._ready() - Existing user, loading last avatar")
|
||||
var lastAvatar = Saving.settings["lastAvatar"]
|
||||
print("DEBUG: lastAvatar setting: ", lastAvatar, " (type: ", typeof(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)
|
||||
var lastAvatar = Saving.settings.get("lastAvatar", "")
|
||||
if lastAvatar != null and lastAvatar != "" and typeof(lastAvatar) == TYPE_STRING and FileAccess.file_exists(lastAvatar):
|
||||
loadAvatar(lastAvatar)
|
||||
else:
|
||||
print("WARNING: lastAvatar setting is invalid, loading default instead")
|
||||
_on_load_dialog_file_selected("default")
|
||||
loadDefaultAvatar()
|
||||
|
||||
$ControlPanel/volumeSlider.value = Saving.settings["volume"]
|
||||
$ControlPanel/sensitiveSlider.value = Saving.settings["sense"]
|
||||
@@ -425,54 +418,35 @@ func _on_load_button_pressed():
|
||||
|
||||
#LOAD AVATAR
|
||||
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
|
||||
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
|
||||
|
||||
print("DEBUG: About to read save data from: ", path)
|
||||
var data = Saving.read_save(path)
|
||||
print("DEBUG: Save data read. Data type: ", typeof(data), ", null check: ", data == null)
|
||||
|
||||
if data == null:
|
||||
print("DEBUG: Save data is null, returning")
|
||||
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()
|
||||
var new = Node2D.new()
|
||||
$OriginMotion.add_child(new)
|
||||
origin = new
|
||||
|
||||
for item in data:
|
||||
print("DEBUG: Processing item: ", item, ", data type: ", typeof(data[item]))
|
||||
if typeof(data[item]) != TYPE_DICTIONARY:
|
||||
print("DEBUG: Skipping item ", item, " - not a dictionary")
|
||||
continue
|
||||
|
||||
if not data[item].has("path"):
|
||||
print("DEBUG: Skipping item ", item, " - no 'path' key. Keys: ", data[item].keys())
|
||||
continue
|
||||
|
||||
var sprite = spriteObject.instantiate()
|
||||
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.parentId = data[item]["parentId"]
|
||||
|
||||
@@ -503,9 +477,9 @@ func _on_load_dialog_file_selected(path):
|
||||
|
||||
if data[item].has("costumeLayers"):
|
||||
sprite.costumeLayers = str_to_var(data[item]["costumeLayers"]).duplicate()
|
||||
if sprite.costumeLayers.size() < 8:
|
||||
for i in range(5):
|
||||
sprite.costumeLayers.append(1)
|
||||
# Ensure we have exactly 10 costume layers
|
||||
while sprite.costumeLayers.size() < 10:
|
||||
sprite.costumeLayers.append(1)
|
||||
|
||||
if data[item].has("stretchAmount"):
|
||||
sprite.stretchAmount = data[item]["stretchAmount"]
|
||||
@@ -527,6 +501,8 @@ func _on_load_dialog_file_selected(path):
|
||||
sprite.spriteOpacity = data[item]["spriteOpacity"]
|
||||
if data[item].has("affectChildrenOpacity"):
|
||||
sprite.affectChildrenOpacity = data[item]["affectChildrenOpacity"]
|
||||
if data[item].has("blendMode"):
|
||||
sprite.blendMode = data[item]["blendMode"]
|
||||
|
||||
# Load wobble sync group (with backward compatibility)
|
||||
if data[item].has("wobbleSyncGroup"):
|
||||
@@ -540,23 +516,8 @@ func _on_load_dialog_file_selected(path):
|
||||
Global.spriteList.updateData()
|
||||
|
||||
# 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:
|
||||
print("DEBUG: Loading wobble sync groups. Data: ", data["wobbleSyncGroups"])
|
||||
await WobbleSyncManager.loadSaveData(data["wobbleSyncGroups"])
|
||||
print("DEBUG: Finished loading wobble sync groups")
|
||||
# Refresh wobble sync UI after groups are loaded
|
||||
if Global.spriteEdit and Global.spriteEdit.wobbleSyncControl:
|
||||
Global.spriteEdit.wobbleSyncControl.updateUI()
|
||||
@@ -566,14 +527,6 @@ func _on_load_dialog_file_selected(path):
|
||||
# Force refresh in case timing issues
|
||||
await get_tree().process_frame
|
||||
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
|
||||
await get_tree().process_frame # Wait for all sprites to be fully initialized
|
||||
@@ -585,7 +538,15 @@ func _on_load_dialog_file_selected(path):
|
||||
Global.pushUpdate("Loaded avatar at: " + path)
|
||||
|
||||
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
|
||||
func _on_save_dialog_file_selected(path):
|
||||
var data = {}
|
||||
@@ -597,6 +558,7 @@ func _on_save_dialog_file_selected(path):
|
||||
data[id] = {}
|
||||
data[id]["type"] = "sprite"
|
||||
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]["identification"] = child.id
|
||||
data[id]["parentId"] = child.parentId
|
||||
@@ -638,6 +600,7 @@ func _on_save_dialog_file_selected(path):
|
||||
|
||||
data[id]["spriteOpacity"] = child.spriteOpacity
|
||||
data[id]["affectChildrenOpacity"] = child.affectChildrenOpacity
|
||||
data[id]["blendMode"] = child.blendMode
|
||||
|
||||
# Save wobble sync group
|
||||
data[id]["wobbleSyncGroup"] = child.wobbleSyncGroup
|
||||
@@ -724,6 +687,7 @@ func _on_duplicate_button_pressed():
|
||||
|
||||
sprite.spriteOpacity = Global.heldSprite.spriteOpacity
|
||||
sprite.affectChildrenOpacity = Global.heldSprite.affectChildrenOpacity
|
||||
sprite.blendMode = Global.heldSprite.blendMode
|
||||
|
||||
# Copy wobble sync group
|
||||
sprite.wobbleSyncGroup = Global.heldSprite.wobbleSyncGroup
|
||||
@@ -754,9 +718,11 @@ func changeCostumeStreamDeck(id: String):
|
||||
"9":changeCostume(9)
|
||||
"10":changeCostume(10)
|
||||
|
||||
func changeCostume(newCostume):
|
||||
func changeCostume(newCostume, preserve_selection: bool = false):
|
||||
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")
|
||||
for sprite in nodes:
|
||||
if sprite.costumeLayers[newCostume-1] == 1:
|
||||
@@ -779,7 +745,7 @@ func moveSpriteMenu(delta):
|
||||
|
||||
var size = get_viewport().get_visible_rect().size
|
||||
|
||||
var windowLength = 1800
|
||||
var windowLength = 2000
|
||||
|
||||
$ViewerArrows/Arrows.position.y = size.y - 25
|
||||
|
||||
|
||||
@@ -14357,26 +14357,38 @@ offset_bottom = -51.0
|
||||
text = "Zoom : 100%"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="VersionLabels" type="Node2D" parent="ControlPanel"]
|
||||
position = Vector2(-284, 59)
|
||||
[node name="VersionLabels" type="Control" parent="ControlPanel"]
|
||||
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"]
|
||||
offset_left = -713.0
|
||||
offset_top = -94.0
|
||||
offset_right = -575.0
|
||||
offset_bottom = -60.0
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="ControlPanel/VersionLabels"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 2
|
||||
anchor_top = 1.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"
|
||||
label_settings = SubResource("LabelSettings_w0f1f")
|
||||
|
||||
[node name="Label2" type="Label" parent="ControlPanel/VersionLabels"]
|
||||
offset_left = -713.0
|
||||
offset_top = -123.0
|
||||
offset_right = -575.0
|
||||
offset_bottom = -89.0
|
||||
[node name="Label2" type="Label" parent="ControlPanel/VersionLabels/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
text = "kaiakairos'"
|
||||
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
|
||||
offset_left = 104.0
|
||||
offset_top = 11.0
|
||||
@@ -14385,15 +14397,13 @@ offset_bottom = 54.0
|
||||
text = "v1.4.5"
|
||||
label_settings = SubResource("LabelSettings_xvf50")
|
||||
|
||||
[node name="Label3" type="Label" parent="ControlPanel/VersionLabels"]
|
||||
offset_left = -566.0
|
||||
offset_top = -93.0
|
||||
offset_right = -428.0
|
||||
offset_bottom = -59.0
|
||||
[node name="Label3" type="Label" parent="ControlPanel/VersionLabels/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
text = "Litruv Edition"
|
||||
label_settings = SubResource("LabelSettings_qg0do")
|
||||
|
||||
[node name="EditControls" type="Node2D" parent="."]
|
||||
visible = false
|
||||
z_index = 4090
|
||||
script = ExtResource("17_bf1ic")
|
||||
|
||||
@@ -14402,7 +14412,7 @@ clip_contents = true
|
||||
anchors_preset = 9
|
||||
anchor_bottom = 1.0
|
||||
offset_top = 70.0
|
||||
offset_right = 250.0
|
||||
offset_right = 273.485
|
||||
offset_bottom = 20070.0
|
||||
grow_vertical = 2
|
||||
size_flags_vertical = 3
|
||||
@@ -14628,7 +14638,6 @@ patch_margin_right = 8
|
||||
patch_margin_bottom = 8
|
||||
|
||||
[node name="Label" type="Label" parent="Tutorial/NinePatchRect"]
|
||||
visible = false
|
||||
layout_mode = 0
|
||||
offset_left = 15.0
|
||||
offset_top = 14.0
|
||||
|
||||
@@ -30,6 +30,7 @@ buses/channel_disable_time=0.0
|
||||
Saving="*res://autoload/saving.gd"
|
||||
Global="*res://autoload/global.gd"
|
||||
WobbleSyncManager="*res://autoload/wobble_sync_manager.gd"
|
||||
BlendModeManager="*res://autoload/blend_mode_manager.gd"
|
||||
ElgatoStreamDeck="*res://addons/godot-streamdeck-addon/singleton.gd"
|
||||
DefaultAvatarData="*res://autoload/defaultAvatarData.gd"
|
||||
|
||||
@@ -49,11 +50,6 @@ window/vsync/vsync_mode=0
|
||||
|
||||
project/assembly_name="PNGTuberPlus"
|
||||
|
||||
[editor]
|
||||
|
||||
version_control/plugin_name="GitPlugin"
|
||||
version_control/autoload_on_startup=true
|
||||
|
||||
[editor_plugins]
|
||||
|
||||
enabled=PackedStringArray("res://addons/godot-streamdeck-addon/plugin.cfg")
|
||||
|
||||
@@ -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 ===")
|
||||
@@ -1 +0,0 @@
|
||||
uid://ca66ai4v1k0sw
|
||||
BIN
ui_scenes/button sprites/checkmark.png
Normal file
BIN
ui_scenes/button sprites/checkmark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
34
ui_scenes/button sprites/checkmark.png.import
Normal file
34
ui_scenes/button sprites/checkmark.png.import
Normal 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
|
||||
105
ui_scenes/components/IMPLEMENTATION_COMPLETE.md
Normal file
105
ui_scenes/components/IMPLEMENTATION_COMPLETE.md
Normal file
@@ -0,0 +1,105 @@
|
||||
# Implementation Complete: Sprite Viewer Slider Components
|
||||
|
||||
## ✅ **Successfully Implemented**
|
||||
|
||||
### **🎯 Components Created**
|
||||
- **LabeledSlider** - Base reusable slider component
|
||||
- **WobbleSlider** - Specialized for wobble parameters with sync support
|
||||
- **ParameterSlider** - Generic parameter binding for sprite properties
|
||||
- **SliderManager** - Centralized management system
|
||||
|
||||
### **🔄 Replaced in sprite_viewer.tscn**
|
||||
|
||||
| **Old Slider** | **New Component** | **Benefits** |
|
||||
|----------------|-------------------|--------------|
|
||||
| Drag Slider | ParameterSlider | Auto label updates, consistent formatting |
|
||||
| X Frequency | WobbleSlider | Sync group support, auto parameter binding |
|
||||
| X Amplitude | WobbleSlider | Consistent wobble handling |
|
||||
| Y Frequency | WobbleSlider | Integrated with wobble sync system |
|
||||
| Y Amplitude | WobbleSlider | Visual feedback for sync status |
|
||||
| Rotation Frequency | WobbleSlider | Proper Hz display formatting |
|
||||
| Rotation Amplitude | WobbleSlider | Percentage display with % suffix |
|
||||
| Rotation Drag | ParameterSlider | Simplified parameter binding |
|
||||
| Squash | ParameterSlider | Decimal precision formatting |
|
||||
| Rotation Limit Min | ParameterSlider | Degree (°) suffix display |
|
||||
| Rotation Limit Max | ParameterSlider | Consistent limit handling |
|
||||
| Animation Frames | ParameterSlider | Integer-only values |
|
||||
| Animation Speed | ParameterSlider | Decimal speed values |
|
||||
| Opacity | ParameterSlider | Custom percentage display |
|
||||
|
||||
### **🎨 Key Features Implemented**
|
||||
|
||||
✅ **Automatic Label Updates** - No more manual label text management
|
||||
✅ **Consistent Formatting** - All sliders use the same styling
|
||||
✅ **Parameter Binding** - Direct sprite property connections
|
||||
✅ **Wobble Sync Integration** - Visual feedback and group handling
|
||||
✅ **Type Safety** - Proper class inheritance
|
||||
✅ **Value Formatting** - Decimal places, suffixes (Hz, %, °)
|
||||
✅ **Signal Management** - Components handle their own connections
|
||||
✅ **Backwards Compatibility** - Old functions cleaned up
|
||||
|
||||
### **🔧 Technical Implementation**
|
||||
|
||||
#### **Sprite Viewer Updates:**
|
||||
- Added SliderManager for centralized control
|
||||
- Implemented `_setup_slider_components()`
|
||||
- Updated `setImage()` to use component system
|
||||
- Modified `updateWobbleControlStates()` for visual sync feedback
|
||||
- Removed redundant old signal handlers
|
||||
- Added new component signal connections
|
||||
|
||||
#### **Scene File Changes:**
|
||||
- Added external resource references for new components
|
||||
- Replaced 14 individual slider+label combinations
|
||||
- Configured each component with appropriate parameters
|
||||
- Removed outdated signal connections
|
||||
- Optimized layout spacing
|
||||
|
||||
#### **Component Features:**
|
||||
- **LabeledSlider**: Base functionality with export properties
|
||||
- **WobbleSlider**: Inherits from LabeledSlider, adds wobble-specific logic
|
||||
- **ParameterSlider**: Generic sprite property binding with special cases
|
||||
- **SliderManager**: Bulk operations and consistent styling
|
||||
|
||||
### **🎯 Benefits Achieved**
|
||||
|
||||
1. **Consistency** - All sliders look and behave identically
|
||||
2. **Maintainability** - One place to modify slider behavior
|
||||
3. **Reusability** - Components work across different scenes
|
||||
4. **Reduced Code** - Less repetitive slider setup and management
|
||||
5. **Better UX** - Automatic updates, proper formatting, visual feedback
|
||||
6. **Type Safety** - Class-based components with proper inheritance
|
||||
|
||||
### **📝 Usage Example**
|
||||
|
||||
**Before (Old System):**
|
||||
```gdscript
|
||||
$WobbleControl/xFrqLabel.text = "x frequency: " + str(value)
|
||||
$WobbleControl/xFrq.value = value
|
||||
# Repeat for every slider...
|
||||
```
|
||||
|
||||
**After (New Components):**
|
||||
```gdscript
|
||||
slider_manager.update_all_sliders(Global.heldSprite)
|
||||
# All sliders update automatically with proper formatting
|
||||
```
|
||||
|
||||
### **🔧 Migration Notes**
|
||||
|
||||
- All old individual sliders have been replaced
|
||||
- Legacy signal handlers removed
|
||||
- Component system handles parameter updates automatically
|
||||
- Visual sync feedback integrated into wobble sliders
|
||||
- Opacity slider has custom percentage display logic
|
||||
|
||||
### **🚀 Ready to Use**
|
||||
|
||||
The sprite viewer now uses a modern, component-based slider system that is:
|
||||
- More maintainable
|
||||
- More consistent
|
||||
- Easier to extend
|
||||
- Better organized
|
||||
- Type-safe
|
||||
|
||||
All sliders in the sprite viewer are now implemented as reusable components! 🎉
|
||||
73
ui_scenes/components/LABEL_FIX.md
Normal file
73
ui_scenes/components/LABEL_FIX.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# 🔧 Fixed: Slider Labels Now Show Correct Text
|
||||
|
||||
## ✅ **Problem Solved**
|
||||
|
||||
The issue was that slider labels were showing "Value" instead of the actual `label_text` property (like "X Frequency", "Drag", etc.) in the Godot editor.
|
||||
|
||||
## 🎯 **Root Cause**
|
||||
|
||||
1. **Editor Execution**: Components needed `@tool` to work properly in the editor
|
||||
2. **Initialization Timing**: Label updates happened before exported properties were set
|
||||
3. **Property Setter Timing**: Label wasn't updated when properties were set by the editor
|
||||
|
||||
## 🔧 **Solution Applied**
|
||||
|
||||
### **1. Added `@tool` Directive**
|
||||
Added `@tool` to all component scripts to enable editor execution:
|
||||
- `LabeledSlider.gd`
|
||||
- `WobbleSlider.gd`
|
||||
- `ParameterSlider.gd`
|
||||
|
||||
### **2. Improved Property Setters**
|
||||
Updated all export property setters to check if the node is ready:
|
||||
```gdscript
|
||||
func set_label_text(new_text: String):
|
||||
label_text = new_text
|
||||
if is_inside_tree():
|
||||
_update_label()
|
||||
else:
|
||||
call_deferred("_update_label")
|
||||
```
|
||||
|
||||
### **3. Enhanced Initialization**
|
||||
- Added `NOTIFICATION_SCENE_INSTANTIATED` handler
|
||||
- Improved `_ready()` function timing
|
||||
- Added deferred label updates for proper timing
|
||||
|
||||
### **4. Fixed Base Template**
|
||||
Updated `LabeledSlider.tscn` to have cleaner default text:
|
||||
```
|
||||
text = "Value" # Instead of "Value: 0"
|
||||
```
|
||||
|
||||
### **5. Robust Label Updates**
|
||||
Enhanced `_update_label()` to handle missing nodes gracefully:
|
||||
```gdscript
|
||||
func _update_label():
|
||||
if not label:
|
||||
label = get_node_or_null("Label")
|
||||
if not label:
|
||||
call_deferred("_update_label")
|
||||
return
|
||||
# ... update logic
|
||||
```
|
||||
|
||||
## 🎉 **Result**
|
||||
|
||||
Now in the Godot editor, all slider labels should properly display:
|
||||
- ✅ "X Frequency: 0.000 Hz"
|
||||
- ✅ "Y Amplitude: 0"
|
||||
- ✅ "Drag: 0.0"
|
||||
- ✅ "Opacity: 100%"
|
||||
- ✅ "Rotational Limit Min: -180°"
|
||||
|
||||
Instead of just "Value" everywhere!
|
||||
|
||||
## 🔄 **How to Test**
|
||||
|
||||
1. Open `sprite_viewer.tscn` in the Godot editor
|
||||
2. Check the slider components in the scene tree
|
||||
3. Labels should now show the correct text for each parameter
|
||||
4. Properties can be modified in the inspector and labels update immediately
|
||||
|
||||
The components now work properly both in the editor preview and at runtime! 🚀
|
||||
140
ui_scenes/components/LabeledSlider.gd
Normal file
140
ui_scenes/components/LabeledSlider.gd
Normal file
@@ -0,0 +1,140 @@
|
||||
@tool
|
||||
## A reusable component that combines a label and slider
|
||||
## Provides consistent formatting and behavior for all sliders in the UI
|
||||
extends VBoxContainer
|
||||
class_name LabeledSlider
|
||||
|
||||
## Emitted when the slider value changes
|
||||
signal value_changed(value: float)
|
||||
|
||||
## The base text that appears in the label (without the value)
|
||||
@export var label_text: String = "Value" : set = set_label_text
|
||||
## The minimum value of the slider
|
||||
@export var min_value: float = 0.0 : set = set_min_value
|
||||
## The maximum value of the slider
|
||||
@export var max_value: float = 100.0 : set = set_max_value
|
||||
## The step value for the slider
|
||||
@export var step: float = 1.0 : set = set_step
|
||||
## The current value of the slider
|
||||
@export var value: float = 0.0 : set = set_value
|
||||
## Whether to show the value in the label
|
||||
@export var show_value_in_label: bool = true : set = set_show_value_in_label
|
||||
## Format string for the value display (e.g., "%.1f" for one decimal place)
|
||||
@export var value_format: String = "%.0f" : set = set_value_format
|
||||
## Optional suffix to add after the value (e.g., "%", "Hz")
|
||||
@export var value_suffix: String = "" : set = set_value_suffix
|
||||
## Whether the slider is editable
|
||||
@export var editable: bool = true : set = set_editable
|
||||
|
||||
@onready var label: Label = $Label
|
||||
@onready var slider: HSlider = $HSlider
|
||||
|
||||
func _ready():
|
||||
if slider:
|
||||
slider.value_changed.connect(_on_slider_value_changed)
|
||||
_update_slider_properties()
|
||||
|
||||
# Ensure label is updated after all properties are set
|
||||
call_deferred("_update_label")
|
||||
|
||||
func _notification(what):
|
||||
if what == NOTIFICATION_SCENE_INSTANTIATED:
|
||||
# Update label when scene is instantiated (important for editor)
|
||||
call_deferred("_update_label")
|
||||
|
||||
## Set the label text
|
||||
func set_label_text(new_text: String):
|
||||
label_text = new_text
|
||||
if is_inside_tree():
|
||||
_update_label()
|
||||
else:
|
||||
call_deferred("_update_label")
|
||||
|
||||
## Set the minimum value
|
||||
func set_min_value(new_min: float):
|
||||
min_value = new_min
|
||||
if is_inside_tree():
|
||||
_update_slider_properties()
|
||||
|
||||
## Set the maximum value
|
||||
func set_max_value(new_max: float):
|
||||
max_value = new_max
|
||||
if is_inside_tree():
|
||||
_update_slider_properties()
|
||||
|
||||
## Set the step value
|
||||
func set_step(new_step: float):
|
||||
step = new_step
|
||||
if is_inside_tree():
|
||||
_update_slider_properties()
|
||||
|
||||
## Set the current value
|
||||
func set_value(new_value: float):
|
||||
value = new_value
|
||||
if slider:
|
||||
slider.value = value
|
||||
if is_inside_tree():
|
||||
_update_label()
|
||||
|
||||
## Set whether to show value in label
|
||||
func set_show_value_in_label(show: bool):
|
||||
show_value_in_label = show
|
||||
if is_inside_tree():
|
||||
_update_label()
|
||||
|
||||
## Set the value format string
|
||||
func set_value_format(format: String):
|
||||
value_format = format
|
||||
if is_inside_tree():
|
||||
_update_label()
|
||||
|
||||
## Set the value suffix
|
||||
func set_value_suffix(suffix: String):
|
||||
value_suffix = suffix
|
||||
if is_inside_tree():
|
||||
_update_label()
|
||||
|
||||
## Set whether the slider is editable
|
||||
func set_editable(is_editable: bool):
|
||||
editable = is_editable
|
||||
if slider:
|
||||
slider.editable = editable
|
||||
|
||||
## Update the slider properties
|
||||
func _update_slider_properties():
|
||||
if not slider:
|
||||
return
|
||||
|
||||
slider.min_value = min_value
|
||||
slider.max_value = max_value
|
||||
slider.step = step
|
||||
slider.value = value
|
||||
slider.editable = editable
|
||||
|
||||
## Update the label text
|
||||
func _update_label():
|
||||
if not label:
|
||||
# If label isn't ready yet, try to find it
|
||||
label = get_node_or_null("Label")
|
||||
if not label:
|
||||
# Call again after a frame when nodes are ready
|
||||
call_deferred("_update_label")
|
||||
return
|
||||
|
||||
var text = label_text
|
||||
if show_value_in_label:
|
||||
var formatted_value = value_format % value
|
||||
text += ": " + formatted_value + value_suffix
|
||||
|
||||
label.text = text
|
||||
|
||||
## Handle slider value changes
|
||||
func _on_slider_value_changed(new_value: float):
|
||||
value = new_value
|
||||
_update_label()
|
||||
value_changed.emit(value)
|
||||
|
||||
## Set the modulate color for visual feedback
|
||||
func set_modulate_color(color: Color):
|
||||
if slider:
|
||||
slider.modulate = color
|
||||
1
ui_scenes/components/LabeledSlider.gd.uid
Normal file
1
ui_scenes/components/LabeledSlider.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://combq5rrok31i
|
||||
22
ui_scenes/components/LabeledSlider.tscn
Normal file
22
ui_scenes/components/LabeledSlider.tscn
Normal file
@@ -0,0 +1,22 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://bxm5q6n8x7p2q"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://combq5rrok31i" path="res://ui_scenes/components/LabeledSlider.gd" id="1_labeled_slider"]
|
||||
|
||||
[node name="LabeledSlider" type="VBoxContainer"]
|
||||
offset_right = 200.0
|
||||
offset_bottom = 40.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 0
|
||||
theme_override_constants/separation = 2
|
||||
script = ExtResource("1_labeled_slider")
|
||||
|
||||
[node name="Label" type="Label" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 0
|
||||
text = "Value: 0"
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="HSlider" type="HSlider" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
81
ui_scenes/components/ParameterSlider.gd
Normal file
81
ui_scenes/components/ParameterSlider.gd
Normal file
@@ -0,0 +1,81 @@
|
||||
@tool
|
||||
## A parameter slider component for general sprite parameters
|
||||
## Handles updating sprite properties with proper formatting
|
||||
extends LabeledSlider
|
||||
class_name ParameterSlider
|
||||
|
||||
## The sprite parameter name this slider controls
|
||||
@export var parameter_name: String = ""
|
||||
## The property path in the sprite object (e.g., "dragSpeed", "rdragStr")
|
||||
@export var sprite_property: String = ""
|
||||
|
||||
## Reference to the sprite being edited
|
||||
var target_sprite = null
|
||||
|
||||
func _ready():
|
||||
super._ready()
|
||||
# Connect to value changes to update parameters
|
||||
value_changed.connect(_on_parameter_value_changed)
|
||||
|
||||
func _notification(what):
|
||||
super._notification(what)
|
||||
if what == NOTIFICATION_SCENE_INSTANTIATED:
|
||||
# Update label for opacity specially
|
||||
if sprite_property == "spriteOpacity":
|
||||
call_deferred("_update_opacity_label")
|
||||
|
||||
## Set the sprite this slider controls
|
||||
func set_target_sprite(sprite):
|
||||
target_sprite = sprite
|
||||
if sprite:
|
||||
_update_from_sprite()
|
||||
|
||||
## Update the slider from the sprite's current values
|
||||
func _update_from_sprite():
|
||||
if not target_sprite or sprite_property == "":
|
||||
return
|
||||
|
||||
var current_value = target_sprite.get(sprite_property)
|
||||
if current_value != null:
|
||||
set_value(current_value)
|
||||
|
||||
# Special handling for opacity display
|
||||
if sprite_property == "spriteOpacity":
|
||||
_update_opacity_label()
|
||||
|
||||
## Update label text with special opacity handling
|
||||
func _update_opacity_label():
|
||||
if sprite_property == "spriteOpacity" and label:
|
||||
var percentage = int(value * 100)
|
||||
label.text = label_text + ": " + str(percentage) + "%"
|
||||
|
||||
## Override base update_label for special cases
|
||||
func _update_label():
|
||||
if sprite_property == "spriteOpacity":
|
||||
_update_opacity_label()
|
||||
else:
|
||||
super._update_label()
|
||||
|
||||
## Handle parameter changes
|
||||
func _on_parameter_value_changed(new_value: float):
|
||||
if not target_sprite or sprite_property == "":
|
||||
return
|
||||
|
||||
|
||||
target_sprite.set(sprite_property, new_value)
|
||||
|
||||
# Call specific update methods if they exist
|
||||
_call_update_method()
|
||||
|
||||
## Call specific update methods for certain parameters
|
||||
func _call_update_method():
|
||||
if not target_sprite:
|
||||
return
|
||||
|
||||
match sprite_property:
|
||||
"spriteOpacity":
|
||||
target_sprite.updateOpacity()
|
||||
"frames":
|
||||
target_sprite.changeFrames()
|
||||
"blendMode":
|
||||
target_sprite.updateBlendMode(value)
|
||||
1
ui_scenes/components/ParameterSlider.gd.uid
Normal file
1
ui_scenes/components/ParameterSlider.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://6maee7dhl7vl
|
||||
9
ui_scenes/components/ParameterSlider.tscn
Normal file
9
ui_scenes/components/ParameterSlider.tscn
Normal file
@@ -0,0 +1,9 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://fgee1pq0yqav"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://bxm5q6n8x7p2q" path="res://ui_scenes/components/LabeledSlider.tscn" id="1_base_slider"]
|
||||
[ext_resource type="Script" uid="uid://6maee7dhl7vl" path="res://ui_scenes/components/ParameterSlider.gd" id="2_parameter_script"]
|
||||
|
||||
[node name="ParameterSlider" instance=ExtResource("1_base_slider")]
|
||||
script = ExtResource("2_parameter_script")
|
||||
parameter_name = ""
|
||||
sprite_property = ""
|
||||
220
ui_scenes/components/README.md
Normal file
220
ui_scenes/components/README.md
Normal file
@@ -0,0 +1,220 @@
|
||||
# Slider Components Documentation
|
||||
|
||||
This document explains the new slider component system that replaces the individual sliders in the sprite viewer with reusable, consistent components.
|
||||
|
||||
## Components Overview
|
||||
|
||||
### 1. LabeledSlider (Base Component)
|
||||
**File:** `ui_scenes/components/LabeledSlider.tscn`
|
||||
**Script:** `ui_scenes/components/LabeledSlider.gd`
|
||||
|
||||
The base slider component that combines a label and slider into one reusable unit.
|
||||
|
||||
**Key Features:**
|
||||
- Automatic label updates with current value
|
||||
- Configurable value formatting
|
||||
- Optional value suffix (%, Hz, etc.)
|
||||
- Consistent styling
|
||||
- Signal emission on value changes
|
||||
|
||||
**Properties:**
|
||||
- `label_text`: Base text for the label
|
||||
- `min_value`, `max_value`, `step`: Slider configuration
|
||||
- `value`: Current value
|
||||
- `show_value_in_label`: Whether to display value in label
|
||||
- `value_format`: Format string for value display (e.g., "%.1f")
|
||||
- `value_suffix`: Suffix to add after value (e.g., "%", "Hz")
|
||||
- `editable`: Whether the slider can be modified
|
||||
|
||||
### 2. WobbleSlider (Specialized Component)
|
||||
**File:** `ui_scenes/components/WobbleSlider.tscn`
|
||||
**Script:** `ui_scenes/components/WobbleSlider.gd`
|
||||
|
||||
Extends LabeledSlider specifically for wobble parameters.
|
||||
|
||||
**Key Features:**
|
||||
- Automatic synchronization with sprite wobble parameters
|
||||
- Visual feedback for sync group status
|
||||
- Integrated with wobble sync system
|
||||
- Parameter-specific configuration
|
||||
|
||||
**Properties:**
|
||||
- `parameter_name`: The wobble parameter this controls ("xFrq", "xAmp", etc.)
|
||||
- All LabeledSlider properties
|
||||
|
||||
### 3. ParameterSlider (Generic Component)
|
||||
**File:** `ui_scenes/components/ParameterSlider.tscn`
|
||||
**Script:** `ui_scenes/components/ParameterSlider.gd`
|
||||
|
||||
Extends LabeledSlider for general sprite parameters.
|
||||
|
||||
**Key Features:**
|
||||
- Generic parameter binding to sprite properties
|
||||
- Automatic update method calling for specific parameters
|
||||
- Property path configuration
|
||||
|
||||
**Properties:**
|
||||
- `sprite_property`: The sprite property to bind to
|
||||
- `parameter_name`: Display name for the parameter
|
||||
- All LabeledSlider properties
|
||||
|
||||
### 4. WobbleControlPanel (Composite Component)
|
||||
**File:** `ui_scenes/components/WobbleControlPanel.tscn`
|
||||
**Script:** `ui_scenes/components/WobbleControlPanel.gd`
|
||||
|
||||
A complete panel containing all wobble sliders.
|
||||
|
||||
**Key Features:**
|
||||
- Pre-configured wobble sliders
|
||||
- Centralized sprite management
|
||||
- Integrated sync feedback
|
||||
- Easy integration with existing wobble sync controls
|
||||
|
||||
### 5. SliderManager (Management System)
|
||||
**Script:** `ui_scenes/components/SliderManager.gd`
|
||||
|
||||
Manages collections of slider components.
|
||||
|
||||
**Key Features:**
|
||||
- Centralized slider updates
|
||||
- Consistent styling application
|
||||
- Bulk operations on sliders
|
||||
- Integration with sprite viewer
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Basic LabeledSlider
|
||||
```gdscript
|
||||
# In your scene
|
||||
@onready var my_slider: LabeledSlider = $LabeledSlider
|
||||
|
||||
func _ready():
|
||||
my_slider.label_text = "Volume"
|
||||
my_slider.min_value = 0.0
|
||||
my_slider.max_value = 100.0
|
||||
my_slider.value_suffix = "%"
|
||||
my_slider.value_changed.connect(_on_volume_changed)
|
||||
|
||||
func _on_volume_changed(value: float):
|
||||
print("Volume changed to: ", value)
|
||||
```
|
||||
|
||||
### WobbleSlider Configuration
|
||||
```gdscript
|
||||
# In scene setup
|
||||
@onready var x_freq_slider: WobbleSlider = $XFrequencySlider
|
||||
|
||||
func _ready():
|
||||
x_freq_slider.label_text = "X Frequency"
|
||||
x_freq_slider.parameter_name = "xFrq"
|
||||
x_freq_slider.min_value = 0.0
|
||||
x_freq_slider.max_value = 10.0
|
||||
x_freq_slider.step = 0.1
|
||||
x_freq_slider.value_format = "%.1f"
|
||||
x_freq_slider.value_suffix = " Hz"
|
||||
|
||||
func set_sprite(sprite):
|
||||
x_freq_slider.set_target_sprite(sprite)
|
||||
```
|
||||
|
||||
### Using WobbleControlPanel
|
||||
```gdscript
|
||||
# Replace individual wobble sliders with the panel
|
||||
@onready var wobble_panel: WobbleControlPanel = $WobbleControlPanel
|
||||
|
||||
func setImage():
|
||||
if Global.heldSprite:
|
||||
wobble_panel.set_target_sprite(Global.heldSprite)
|
||||
wobble_panel.update_from_sprite()
|
||||
```
|
||||
|
||||
### SliderManager Integration
|
||||
```gdscript
|
||||
# In sprite viewer
|
||||
var slider_manager: SliderManager
|
||||
|
||||
func _ready():
|
||||
slider_manager = SliderManager.new(self)
|
||||
_setup_sliders()
|
||||
|
||||
func _setup_sliders():
|
||||
# Add sliders to manager
|
||||
slider_manager.add_slider($DragSlider)
|
||||
slider_manager.add_slider($OpacitySlider)
|
||||
# etc.
|
||||
|
||||
func setImage():
|
||||
if slider_manager:
|
||||
slider_manager.update_all_sliders(Global.heldSprite)
|
||||
```
|
||||
|
||||
## Migration from Legacy Sliders
|
||||
|
||||
### Step 1: Replace Scene Nodes
|
||||
Replace individual HSlider + Label combinations with LabeledSlider instances:
|
||||
|
||||
**Before:**
|
||||
```
|
||||
- Node2D
|
||||
- Label (for "x frequency: 5.0")
|
||||
- HSlider (for value input)
|
||||
```
|
||||
|
||||
**After:**
|
||||
```
|
||||
- Node2D
|
||||
- LabeledSlider (handles both label and slider)
|
||||
```
|
||||
|
||||
### Step 2: Update Script References
|
||||
**Before:**
|
||||
```gdscript
|
||||
$WobbleControl/xFrqLabel.text = "x frequency: " + str(value)
|
||||
$WobbleControl/xFrq.value = value
|
||||
```
|
||||
|
||||
**After:**
|
||||
```gdscript
|
||||
$WobbleControl/XFrequencySlider.set_value(value)
|
||||
# Label updates automatically
|
||||
```
|
||||
|
||||
### Step 3: Connect Signals
|
||||
**Before:**
|
||||
```gdscript
|
||||
func _on_x_frq_value_changed(value):
|
||||
Global.heldSprite.xFrq = value
|
||||
```
|
||||
|
||||
**After:**
|
||||
```gdscript
|
||||
# Handled automatically by WobbleSlider component
|
||||
# Or connect to the component's value_changed signal
|
||||
```
|
||||
|
||||
## Benefits
|
||||
|
||||
1. **Consistency**: All sliders look and behave the same way
|
||||
2. **Maintainability**: Changes to slider behavior only need to be made in one place
|
||||
3. **Reusability**: Components can be used across different parts of the application
|
||||
4. **Type Safety**: Proper class inheritance with specific functionality
|
||||
5. **Reduced Code**: Less repetitive slider setup code
|
||||
6. **Better Organization**: Clear separation of concerns
|
||||
|
||||
## Integration Notes
|
||||
|
||||
- The new system is designed to be backwards compatible
|
||||
- Legacy slider code is maintained as fallback
|
||||
- Components can be gradually adopted
|
||||
- SliderManager provides centralized control
|
||||
- Easy to extend for new parameter types
|
||||
|
||||
## Custom Styling
|
||||
|
||||
To apply consistent styling across all sliders, modify the base LabeledSlider component or use the SliderManager's styling methods:
|
||||
|
||||
```gdscript
|
||||
func apply_custom_theme():
|
||||
for slider in slider_manager.sliders:
|
||||
slider.slider.add_theme_stylebox_override("slider", custom_style)
|
||||
```
|
||||
47
ui_scenes/components/SampleWobbleControl.tscn
Normal file
47
ui_scenes/components/SampleWobbleControl.tscn
Normal file
@@ -0,0 +1,47 @@
|
||||
[gd_scene load_steps=4 format=4 uid="uid://bh7n2k1q5m8ya"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://c8q7x2n9p4r5s" path="res://ui_scenes/components/WobbleSlider.tscn" id="1_wobble_x_frq"]
|
||||
[ext_resource type="PackedScene" uid="uid://c8q7x2n9p4r5s" path="res://ui_scenes/components/WobbleSlider.tscn" id="2_wobble_x_amp"]
|
||||
[ext_resource type="PackedScene" uid="uid://d9t8y3m0k6l1n" path="res://ui_scenes/components/ParameterSlider.tscn" id="3_drag_slider"]
|
||||
|
||||
[node name="SampleWobbleControl" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_right = -1680.0
|
||||
offset_bottom = -1080.0
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
|
||||
[node name="XFrequencySlider" parent="VBoxContainer" instance=ExtResource("1_wobble_x_frq")]
|
||||
layout_mode = 2
|
||||
label_text = "X Frequency"
|
||||
min_value = 0.0
|
||||
max_value = 10.0
|
||||
step = 0.1
|
||||
value_format = "%.1f"
|
||||
value_suffix = " Hz"
|
||||
parameter_name = "xFrq"
|
||||
|
||||
[node name="XAmplitudeSlider" parent="VBoxContainer" instance=ExtResource("2_wobble_x_amp")]
|
||||
layout_mode = 2
|
||||
label_text = "X Amplitude"
|
||||
min_value = 0.0
|
||||
max_value = 100.0
|
||||
step = 1.0
|
||||
value_format = "%.0f"
|
||||
parameter_name = "xAmp"
|
||||
|
||||
[node name="DragSlider" parent="VBoxContainer" instance=ExtResource("3_drag_slider")]
|
||||
layout_mode = 2
|
||||
label_text = "Drag"
|
||||
min_value = 0.0
|
||||
max_value = 10.0
|
||||
step = 0.1
|
||||
value_format = "%.1f"
|
||||
sprite_property = "dragSpeed"
|
||||
0
ui_scenes/components/SliderConfig.gd
Normal file
0
ui_scenes/components/SliderConfig.gd
Normal file
1
ui_scenes/components/SliderConfig.gd.uid
Normal file
1
ui_scenes/components/SliderConfig.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://db4gh5lnm7j5k
|
||||
60
ui_scenes/components/SliderManager.gd
Normal file
60
ui_scenes/components/SliderManager.gd
Normal file
@@ -0,0 +1,60 @@
|
||||
## Manages all slider components in the sprite viewer
|
||||
## Provides a centralized way to update and configure sliders
|
||||
extends RefCounted
|
||||
class_name SliderManager
|
||||
|
||||
## Array of all slider components
|
||||
var sliders: Array[LabeledSlider] = []
|
||||
## Reference to the sprite viewer
|
||||
var sprite_viewer = null
|
||||
|
||||
func _init(viewer):
|
||||
sprite_viewer = viewer
|
||||
|
||||
## Add a slider to be managed
|
||||
func add_slider(slider: LabeledSlider):
|
||||
if slider not in sliders:
|
||||
sliders.append(slider)
|
||||
|
||||
## Remove a slider from management
|
||||
func remove_slider(slider: LabeledSlider):
|
||||
sliders.erase(slider)
|
||||
|
||||
## Update all sliders with the current sprite data
|
||||
func update_all_sliders(sprite):
|
||||
for slider in sliders:
|
||||
if slider is WobbleSlider:
|
||||
slider.set_target_sprite(sprite)
|
||||
elif slider is ParameterSlider:
|
||||
slider.set_target_sprite(sprite)
|
||||
|
||||
## Update wobble control states based on sync status
|
||||
func update_wobble_control_states(sprite):
|
||||
if not sprite:
|
||||
return
|
||||
|
||||
var is_synced = sprite.isSynced()
|
||||
|
||||
for slider in sliders:
|
||||
if slider is WobbleSlider:
|
||||
# Keep wobble controls enabled even when synced (they edit group settings)
|
||||
slider.set_editable(true)
|
||||
|
||||
# Visual feedback for sync state - slight tint to indicate group editing
|
||||
var tint = Color(1.0, 1.0, 0.9) if is_synced else Color.WHITE
|
||||
slider.set_modulate_color(tint)
|
||||
|
||||
## Get a slider by its parameter name
|
||||
func get_slider_by_parameter(parameter_name: String) -> LabeledSlider:
|
||||
for slider in sliders:
|
||||
if slider is WobbleSlider and slider.parameter_name == parameter_name:
|
||||
return slider
|
||||
elif slider is ParameterSlider and slider.sprite_property == parameter_name:
|
||||
return slider
|
||||
return null
|
||||
|
||||
## Apply consistent styling to all sliders
|
||||
func apply_consistent_styling():
|
||||
for slider in sliders:
|
||||
# Apply any consistent styling here
|
||||
pass
|
||||
1
ui_scenes/components/SliderManager.gd.uid
Normal file
1
ui_scenes/components/SliderManager.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://c0rejv3uadhfh
|
||||
0
ui_scenes/components/SpriteSliderFactory.gd
Normal file
0
ui_scenes/components/SpriteSliderFactory.gd
Normal file
1
ui_scenes/components/SpriteSliderFactory.gd.uid
Normal file
1
ui_scenes/components/SpriteSliderFactory.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://c8ssre2afcija
|
||||
63
ui_scenes/components/WobbleControlPanel.gd
Normal file
63
ui_scenes/components/WobbleControlPanel.gd
Normal file
@@ -0,0 +1,63 @@
|
||||
## A complete wobble control panel using the new slider components
|
||||
## Provides a consistent interface for all wobble parameters
|
||||
extends Control
|
||||
class_name WobbleControlPanel
|
||||
|
||||
## Reference to all wobble sliders
|
||||
@onready var x_frequency_slider: WobbleSlider = $VBoxContainer/XFrequencySlider
|
||||
@onready var x_amplitude_slider: WobbleSlider = $VBoxContainer/XAmplitudeSlider
|
||||
@onready var y_frequency_slider: WobbleSlider = $VBoxContainer/YFrequencySlider
|
||||
@onready var y_amplitude_slider: WobbleSlider = $VBoxContainer/YAmplitudeSlider
|
||||
@onready var r_frequency_slider: WobbleSlider = $VBoxContainer/RFrequencySlider
|
||||
@onready var r_amplitude_slider: WobbleSlider = $VBoxContainer/RAmplitudeSlider
|
||||
|
||||
## Array of all wobble sliders for easy iteration
|
||||
var wobble_sliders: Array[WobbleSlider] = []
|
||||
|
||||
## The sprite currently being edited
|
||||
var target_sprite = null
|
||||
|
||||
func _ready():
|
||||
# Collect all wobble sliders
|
||||
wobble_sliders = [
|
||||
x_frequency_slider,
|
||||
x_amplitude_slider,
|
||||
y_frequency_slider,
|
||||
y_amplitude_slider,
|
||||
r_frequency_slider,
|
||||
r_amplitude_slider
|
||||
]
|
||||
|
||||
# Connect to wobble sync control if it exists
|
||||
var wobble_sync_control = find_child("WobbleSyncControl")
|
||||
if wobble_sync_control:
|
||||
_connect_wobble_sync_signals(wobble_sync_control)
|
||||
|
||||
## Set the target sprite for all sliders
|
||||
func set_target_sprite(sprite):
|
||||
target_sprite = sprite
|
||||
for slider in wobble_sliders:
|
||||
slider.set_target_sprite(sprite)
|
||||
|
||||
## Update all sliders from the current sprite
|
||||
func update_from_sprite():
|
||||
if target_sprite:
|
||||
for slider in wobble_sliders:
|
||||
slider._update_from_sprite()
|
||||
|
||||
## Update visual feedback for sync status
|
||||
func update_sync_feedback():
|
||||
for slider in wobble_sliders:
|
||||
slider._update_sync_feedback()
|
||||
|
||||
## Connect signals from wobble sync control
|
||||
func _connect_wobble_sync_signals(wobble_sync_control):
|
||||
# Connect value change signals to update wobble sync control
|
||||
for slider in wobble_sliders:
|
||||
slider.value_changed.connect(_on_wobble_parameter_changed)
|
||||
|
||||
## Handle wobble parameter changes to update sync control
|
||||
func _on_wobble_parameter_changed(value: float):
|
||||
var wobble_sync_control = find_child("WobbleSyncControl")
|
||||
if wobble_sync_control and wobble_sync_control.has_method("updateUI"):
|
||||
wobble_sync_control.updateUI()
|
||||
1
ui_scenes/components/WobbleControlPanel.gd.uid
Normal file
1
ui_scenes/components/WobbleControlPanel.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cq2ltb5rxsr08
|
||||
75
ui_scenes/components/WobbleControlPanel.tscn
Normal file
75
ui_scenes/components/WobbleControlPanel.tscn
Normal file
@@ -0,0 +1,75 @@
|
||||
[gd_scene load_steps=8 format=4 uid="uid://bp2q8r4l6lam3"]
|
||||
|
||||
[ext_resource type="Script" path="res://ui_scenes/components/WobbleControlPanel.gd" id="1_wobble_panel_script"]
|
||||
[ext_resource type="PackedScene" uid="uid://c8q7x2n9p4r5s" path="res://ui_scenes/components/WobbleSlider.tscn" id="2_wobble_slider"]
|
||||
|
||||
[node name="WobbleControlPanel" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
script = ExtResource("1_wobble_panel_script")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
|
||||
[node name="XFrequencySlider" parent="VBoxContainer" instance=ExtResource("2_wobble_slider")]
|
||||
layout_mode = 2
|
||||
label_text = "X Frequency"
|
||||
min_value = 0.0
|
||||
max_value = 10.0
|
||||
step = 0.1
|
||||
value_format = "%.1f"
|
||||
value_suffix = " Hz"
|
||||
parameter_name = "xFrq"
|
||||
|
||||
[node name="XAmplitudeSlider" parent="VBoxContainer" instance=ExtResource("2_wobble_slider")]
|
||||
layout_mode = 2
|
||||
label_text = "X Amplitude"
|
||||
min_value = 0.0
|
||||
max_value = 100.0
|
||||
step = 1.0
|
||||
value_format = "%.0f"
|
||||
parameter_name = "xAmp"
|
||||
|
||||
[node name="YFrequencySlider" parent="VBoxContainer" instance=ExtResource("2_wobble_slider")]
|
||||
layout_mode = 2
|
||||
label_text = "Y Frequency"
|
||||
min_value = 0.0
|
||||
max_value = 10.0
|
||||
step = 0.1
|
||||
value_format = "%.1f"
|
||||
value_suffix = " Hz"
|
||||
parameter_name = "yFrq"
|
||||
|
||||
[node name="YAmplitudeSlider" parent="VBoxContainer" instance=ExtResource("2_wobble_slider")]
|
||||
layout_mode = 2
|
||||
label_text = "Y Amplitude"
|
||||
min_value = 0.0
|
||||
max_value = 100.0
|
||||
step = 1.0
|
||||
value_format = "%.0f"
|
||||
parameter_name = "yAmp"
|
||||
|
||||
[node name="RFrequencySlider" parent="VBoxContainer" instance=ExtResource("2_wobble_slider")]
|
||||
layout_mode = 2
|
||||
label_text = "Rotation Frequency"
|
||||
min_value = 0.0
|
||||
max_value = 10.0
|
||||
step = 0.1
|
||||
value_format = "%.1f"
|
||||
value_suffix = " Hz"
|
||||
parameter_name = "rFrq"
|
||||
|
||||
[node name="RAmplitudeSlider" parent="VBoxContainer" instance=ExtResource("2_wobble_slider")]
|
||||
layout_mode = 2
|
||||
label_text = "Rotation Amplitude"
|
||||
min_value = 0.0
|
||||
max_value = 100.0
|
||||
step = 1.0
|
||||
value_format = "%.0f"
|
||||
value_suffix = "%"
|
||||
parameter_name = "rAmp"
|
||||
60
ui_scenes/components/WobbleSlider.gd
Normal file
60
ui_scenes/components/WobbleSlider.gd
Normal file
@@ -0,0 +1,60 @@
|
||||
@tool
|
||||
## A specialized slider component for wobble parameters
|
||||
## Automatically handles syncing with wobble groups and provides appropriate feedback
|
||||
extends LabeledSlider
|
||||
class_name WobbleSlider
|
||||
|
||||
## The wobble parameter name this slider controls
|
||||
@export var parameter_name: String = ""
|
||||
|
||||
## Reference to the sprite being edited
|
||||
var target_sprite = null
|
||||
|
||||
func _ready():
|
||||
super._ready()
|
||||
# Connect to value changes to update wobble parameters
|
||||
value_changed.connect(_on_wobble_value_changed)
|
||||
|
||||
## Set the sprite this slider controls
|
||||
func set_target_sprite(sprite):
|
||||
target_sprite = sprite
|
||||
if sprite:
|
||||
_update_from_sprite()
|
||||
|
||||
## Update the slider from the sprite's current values
|
||||
func _update_from_sprite():
|
||||
if not target_sprite or parameter_name == "":
|
||||
return
|
||||
|
||||
match parameter_name:
|
||||
"xFrq":
|
||||
set_value(target_sprite.xFrq)
|
||||
"xAmp":
|
||||
set_value(target_sprite.xAmp)
|
||||
"yFrq":
|
||||
set_value(target_sprite.yFrq)
|
||||
"yAmp":
|
||||
set_value(target_sprite.yAmp)
|
||||
"rFrq":
|
||||
set_value(target_sprite.rFrq)
|
||||
"rAmp":
|
||||
set_value(target_sprite.rAmp)
|
||||
|
||||
## Handle wobble parameter changes
|
||||
func _on_wobble_value_changed(new_value: float):
|
||||
if not target_sprite or parameter_name == "":
|
||||
return
|
||||
|
||||
target_sprite.updateWobbleParameter(parameter_name, new_value)
|
||||
|
||||
# Update visual feedback for sync status
|
||||
_update_sync_feedback()
|
||||
|
||||
## Update visual feedback based on sync status
|
||||
func _update_sync_feedback():
|
||||
if not target_sprite:
|
||||
return
|
||||
|
||||
var is_synced = target_sprite.isSynced()
|
||||
var tint = Color(1.0, 1.0, 0.9) if is_synced else Color.WHITE
|
||||
set_modulate_color(tint)
|
||||
1
ui_scenes/components/WobbleSlider.gd.uid
Normal file
1
ui_scenes/components/WobbleSlider.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://fsc2h7ajjcbs
|
||||
19
ui_scenes/components/WobbleSlider.tscn
Normal file
19
ui_scenes/components/WobbleSlider.tscn
Normal file
@@ -0,0 +1,19 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://c8q7x2oap4r5s"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://bxm5q6n8x7p2q" path="res://ui_scenes/components/LabeledSlider.tscn" id="1_base_slider"]
|
||||
[ext_resource type="Script" uid="uid://fsc2h7ajjcbs" path="res://ui_scenes/components/WobbleSlider.gd" id="2_wobble_script"]
|
||||
|
||||
[node name="WobbleSlider" instance=ExtResource("1_base_slider")]
|
||||
script = ExtResource("2_wobble_script")
|
||||
parameter_name = ""
|
||||
|
||||
[node name="Label" parent="." index="0"]
|
||||
anchors_preset = 10
|
||||
grow_horizontal = 2
|
||||
text = "Value: 0"
|
||||
horizontal_alignment = 0
|
||||
|
||||
[node name="HSlider" parent="." index="1"]
|
||||
anchors_preset = 12
|
||||
grow_horizontal = 0
|
||||
grow_vertical = 0
|
||||
122
ui_scenes/selectedSprite/opacity_blend.gdshader
Normal file
122
ui_scenes/selectedSprite/opacity_blend.gdshader
Normal file
@@ -0,0 +1,122 @@
|
||||
shader_type canvas_item;
|
||||
|
||||
uniform float opacity : hint_range(0.0, 1.0) = 1.0;
|
||||
uniform int blend_mode : hint_range(0, 11) = 0;
|
||||
|
||||
// Blend mode constants:
|
||||
// 0: Normal
|
||||
// 1: Multiply
|
||||
// 2: Screen
|
||||
// 3: Overlay
|
||||
// 4: Soft Light
|
||||
// 5: Hard Light
|
||||
// 6: Color Dodge
|
||||
// 7: Color Burn
|
||||
// 8: Darken
|
||||
// 9: Lighten
|
||||
// 10: Add (Linear Dodge)
|
||||
// 11: Subtract
|
||||
|
||||
vec3 multiply_blend(vec3 base, vec3 overlay) {
|
||||
return base * overlay;
|
||||
}
|
||||
|
||||
vec3 screen_blend(vec3 base, vec3 overlay) {
|
||||
return 1.0 - (1.0 - base) * (1.0 - overlay);
|
||||
}
|
||||
|
||||
vec3 overlay_blend(vec3 base, vec3 overlay) {
|
||||
vec3 result;
|
||||
result.r = base.r < 0.5 ? 2.0 * base.r * overlay.r : 1.0 - 2.0 * (1.0 - base.r) * (1.0 - overlay.r);
|
||||
result.g = base.g < 0.5 ? 2.0 * base.g * overlay.g : 1.0 - 2.0 * (1.0 - base.g) * (1.0 - overlay.g);
|
||||
result.b = base.b < 0.5 ? 2.0 * base.b * overlay.b : 1.0 - 2.0 * (1.0 - base.b) * (1.0 - overlay.b);
|
||||
return result;
|
||||
}
|
||||
|
||||
vec3 soft_light_blend(vec3 base, vec3 overlay) {
|
||||
vec3 result;
|
||||
result.r = overlay.r < 0.5 ? 2.0 * base.r * overlay.r + base.r * base.r * (1.0 - 2.0 * overlay.r) :
|
||||
sqrt(base.r) * (2.0 * overlay.r - 1.0) + 2.0 * base.r * (1.0 - overlay.r);
|
||||
result.g = overlay.g < 0.5 ? 2.0 * base.g * overlay.g + base.g * base.g * (1.0 - 2.0 * overlay.g) :
|
||||
sqrt(base.g) * (2.0 * overlay.g - 1.0) + 2.0 * base.g * (1.0 - overlay.g);
|
||||
result.b = overlay.b < 0.5 ? 2.0 * base.b * overlay.b + base.b * base.b * (1.0 - 2.0 * overlay.b) :
|
||||
sqrt(base.b) * (2.0 * overlay.b - 1.0) + 2.0 * base.b * (1.0 - overlay.b);
|
||||
return result;
|
||||
}
|
||||
|
||||
vec3 hard_light_blend(vec3 base, vec3 overlay) {
|
||||
vec3 result;
|
||||
result.r = overlay.r < 0.5 ? 2.0 * base.r * overlay.r : 1.0 - 2.0 * (1.0 - base.r) * (1.0 - overlay.r);
|
||||
result.g = overlay.g < 0.5 ? 2.0 * base.g * overlay.g : 1.0 - 2.0 * (1.0 - base.g) * (1.0 - overlay.g);
|
||||
result.b = overlay.b < 0.5 ? 2.0 * base.b * overlay.b : 1.0 - 2.0 * (1.0 - base.b) * (1.0 - overlay.b);
|
||||
return result;
|
||||
}
|
||||
|
||||
vec3 color_dodge_blend(vec3 base, vec3 overlay) {
|
||||
vec3 result;
|
||||
result.r = overlay.r >= 1.0 ? 1.0 : min(1.0, base.r / (1.0 - overlay.r));
|
||||
result.g = overlay.g >= 1.0 ? 1.0 : min(1.0, base.g / (1.0 - overlay.g));
|
||||
result.b = overlay.b >= 1.0 ? 1.0 : min(1.0, base.b / (1.0 - overlay.b));
|
||||
return result;
|
||||
}
|
||||
|
||||
vec3 color_burn_blend(vec3 base, vec3 overlay) {
|
||||
vec3 result;
|
||||
result.r = overlay.r <= 0.0 ? 0.0 : max(0.0, 1.0 - (1.0 - base.r) / overlay.r);
|
||||
result.g = overlay.g <= 0.0 ? 0.0 : max(0.0, 1.0 - (1.0 - base.g) / overlay.g);
|
||||
result.b = overlay.b <= 0.0 ? 0.0 : max(0.0, 1.0 - (1.0 - base.b) / overlay.b);
|
||||
return result;
|
||||
}
|
||||
|
||||
vec3 darken_blend(vec3 base, vec3 overlay) {
|
||||
return min(base, overlay);
|
||||
}
|
||||
|
||||
vec3 lighten_blend(vec3 base, vec3 overlay) {
|
||||
return max(base, overlay);
|
||||
}
|
||||
|
||||
vec3 add_blend(vec3 base, vec3 overlay) {
|
||||
return min(vec3(1.0), base + overlay);
|
||||
}
|
||||
|
||||
vec3 subtract_blend(vec3 base, vec3 overlay) {
|
||||
return max(vec3(0.0), base - overlay);
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
vec4 tex_color = texture(TEXTURE, UV);
|
||||
|
||||
// Apply blend mode (only to RGB, preserve alpha channel behavior)
|
||||
vec3 final_color;
|
||||
|
||||
if (blend_mode == 0) { // Normal
|
||||
final_color = tex_color.rgb;
|
||||
} else if (blend_mode == 1) { // Multiply
|
||||
final_color = multiply_blend(vec3(1.0), tex_color.rgb);
|
||||
} else if (blend_mode == 2) { // Screen
|
||||
final_color = screen_blend(vec3(0.0), tex_color.rgb);
|
||||
} else if (blend_mode == 3) { // Overlay
|
||||
final_color = overlay_blend(vec3(0.5), tex_color.rgb);
|
||||
} else if (blend_mode == 4) { // Soft Light
|
||||
final_color = soft_light_blend(vec3(0.5), tex_color.rgb);
|
||||
} else if (blend_mode == 5) { // Hard Light
|
||||
final_color = hard_light_blend(vec3(0.5), tex_color.rgb);
|
||||
} else if (blend_mode == 6) { // Color Dodge
|
||||
final_color = color_dodge_blend(vec3(0.0), tex_color.rgb);
|
||||
} else if (blend_mode == 7) { // Color Burn
|
||||
final_color = color_burn_blend(vec3(1.0), tex_color.rgb);
|
||||
} else if (blend_mode == 8) { // Darken
|
||||
final_color = darken_blend(vec3(1.0), tex_color.rgb);
|
||||
} else if (blend_mode == 9) { // Lighten
|
||||
final_color = lighten_blend(vec3(0.0), tex_color.rgb);
|
||||
} else if (blend_mode == 10) { // Add (Linear Dodge)
|
||||
final_color = add_blend(vec3(0.0), tex_color.rgb);
|
||||
} else if (blend_mode == 11) { // Subtract
|
||||
final_color = subtract_blend(vec3(1.0), tex_color.rgb);
|
||||
} else {
|
||||
final_color = tex_color.rgb;
|
||||
}
|
||||
|
||||
COLOR = vec4(final_color, tex_color.a * opacity);
|
||||
}
|
||||
1
ui_scenes/selectedSprite/opacity_blend.gdshader.uid
Normal file
1
ui_scenes/selectedSprite/opacity_blend.gdshader.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dy03x5g364v80
|
||||
@@ -6,6 +6,8 @@ var type = "sprite"
|
||||
var imageData = null
|
||||
var tex = null
|
||||
@export var path = ""
|
||||
## Custom display name for the sprite (separate from file path)
|
||||
var displayName = ""
|
||||
|
||||
var loadedImageData = null
|
||||
|
||||
@@ -28,7 +30,7 @@ var imageSize = Vector2.ZERO
|
||||
@onready var wob = $WobbleOrigin
|
||||
|
||||
@onready var outlineScene = preload("res://ui_scenes/selectedSprite/outline.tscn")
|
||||
@onready var opacityShader = preload("res://ui_scenes/selectedSprite/opacity.gdshader")
|
||||
@onready var opacityBlendShader = preload("res://ui_scenes/selectedSprite/opacity_blend.gdshader")
|
||||
|
||||
var opacityMaterial = null
|
||||
|
||||
@@ -90,6 +92,7 @@ var time = 0.0 # Time accumulator for physics calculations
|
||||
#Opacity
|
||||
var spriteOpacity = 1.0
|
||||
var affectChildrenOpacity = false
|
||||
var blendMode = 0 # Default to Normal blend mode
|
||||
|
||||
#Vis toggle
|
||||
var toggle = "null"
|
||||
@@ -318,8 +321,6 @@ func calculateParentOpacityMultiplier():
|
||||
|
||||
## Update wobble parameters and sync to group if needed
|
||||
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
|
||||
match parameter:
|
||||
@@ -330,17 +331,15 @@ func updateWobbleParameter(parameter: String, value: float):
|
||||
"rFrq": rFrq = 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 wobbleSyncGroup != "" and WobbleSyncManager:
|
||||
print("DEBUG: Sprite is in sync group '", wobbleSyncGroup, "' - updating group wobble parameters")
|
||||
WobbleSyncManager.updateGroupWobble(wobbleSyncGroup, xFrq, xAmp, yFrq, yAmp, rFrq, rAmp)
|
||||
|
||||
# Don't refresh UI immediately - let the sync process complete first
|
||||
# The wobble sync control will handle UI updates when needed
|
||||
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
|
||||
func isSynced() -> bool:
|
||||
@@ -517,14 +516,21 @@ func updateShaderOpacity():
|
||||
var editorAlpha = sprite.modulate.a
|
||||
var finalOpacity = totalOpacity * editorAlpha
|
||||
|
||||
# Always apply the opacity shader to ensure it's loaded
|
||||
# Always apply the opacity blend shader to ensure it's loaded
|
||||
if opacityMaterial == null:
|
||||
opacityMaterial = ShaderMaterial.new()
|
||||
opacityMaterial.shader = opacityShader
|
||||
opacityMaterial.shader = opacityBlendShader
|
||||
|
||||
opacityMaterial.set_shader_parameter("opacity", finalOpacity)
|
||||
opacityMaterial.set_shader_parameter("blend_mode", blendMode)
|
||||
sprite.material = opacityMaterial
|
||||
|
||||
## Update the blend mode and refresh the shader
|
||||
func updateBlendMode(newBlendMode: int):
|
||||
if BlendModeManager.is_valid_blend_mode(newBlendMode):
|
||||
blendMode = newBlendMode
|
||||
updateShaderOpacity() # Refresh shader with new blend mode
|
||||
|
||||
func updateChildrenOpacityRecursively():
|
||||
var linkedSprites = getAllLinkedSprites()
|
||||
for child in linkedSprites:
|
||||
|
||||
@@ -211,7 +211,6 @@ func _on_stream_deck_check_toggled(button_pressed):
|
||||
func _on_experimental_mic_loudness_toggle(checked):
|
||||
Global.experimentalMicLoudness = checked
|
||||
Saving.settings["experimentalMicLoudness"] = checked
|
||||
print("Experimental mic loudness: ", checked)
|
||||
|
||||
|
||||
func _process(delta):
|
||||
|
||||
@@ -5,19 +5,137 @@ extends Node2D
|
||||
|
||||
@onready var parentSpin = $SubViewportContainer2/SubViewport/Node3D/Sprite3D
|
||||
|
||||
@onready var spriteRotDisplay = $RotationalLimits/RotBack/SpriteDisplay
|
||||
@onready var spriteRotDisplay = $VBoxContainer/RotationalLimitsSection/RotBack/SpriteDisplay
|
||||
|
||||
|
||||
@onready var coverCollider = $Area2D/CollisionShape2D
|
||||
@onready var wobbleSyncControl = null # Will be assigned in _ready()
|
||||
@onready var blendModeDropdown = $VBoxContainer/RenderingSection/blendModeContainer/blendModeDropdown
|
||||
|
||||
## Slider management system
|
||||
var slider_manager: SliderManager = null
|
||||
|
||||
func _ready():
|
||||
Global.spriteEdit = self
|
||||
|
||||
# Initialize slider management system
|
||||
slider_manager = SliderManager.new(self)
|
||||
_setup_slider_components()
|
||||
|
||||
# Find the wobble sync control node
|
||||
wobbleSyncControl = find_child("WobbleSyncControl")
|
||||
if wobbleSyncControl == null:
|
||||
print("Warning: WobbleSyncControl node not found in sprite viewer")
|
||||
|
||||
# Set up blend mode dropdown
|
||||
setupBlendModeDropdown()
|
||||
|
||||
## Set up all slider components
|
||||
func _setup_slider_components():
|
||||
# Initialize all the new slider components and add them to the manager
|
||||
_setup_drag_slider()
|
||||
_setup_wobble_sliders()
|
||||
_setup_rotation_sliders()
|
||||
_setup_limit_sliders()
|
||||
_setup_animation_sliders()
|
||||
_setup_opacity_slider()
|
||||
|
||||
## Set up drag slider
|
||||
func _setup_drag_slider():
|
||||
var drag_slider = $VBoxContainer/PhysicsSection/DragSlider
|
||||
if drag_slider:
|
||||
slider_manager.add_slider(drag_slider)
|
||||
drag_slider.value_changed.connect(_on_drag_slider_value_changed)
|
||||
|
||||
## Set up wobble sliders
|
||||
func _setup_wobble_sliders():
|
||||
var wobble_sliders = [
|
||||
$VBoxContainer/WobbleSection/xFrqSlider,
|
||||
$VBoxContainer/WobbleSection/xAmpSlider,
|
||||
$VBoxContainer/WobbleSection/yFrqSlider,
|
||||
$VBoxContainer/WobbleSection/yAmpSlider,
|
||||
$VBoxContainer/WobbleSection/rFrqSlider,
|
||||
$VBoxContainer/WobbleSection/rAmpSlider
|
||||
]
|
||||
|
||||
for slider in wobble_sliders:
|
||||
if slider:
|
||||
slider_manager.add_slider(slider)
|
||||
slider.value_changed.connect(_on_wobble_slider_changed)
|
||||
|
||||
## Set up rotation sliders
|
||||
func _setup_rotation_sliders():
|
||||
var rotation_sliders = [
|
||||
$VBoxContainer/PhysicsSection/rDragSlider,
|
||||
$VBoxContainer/PhysicsSection/squashSlider
|
||||
]
|
||||
|
||||
for slider in rotation_sliders:
|
||||
if slider:
|
||||
slider_manager.add_slider(slider)
|
||||
slider.value_changed.connect(_on_rotation_slider_changed)
|
||||
|
||||
## Set up rotational limit sliders
|
||||
func _setup_limit_sliders():
|
||||
var limit_sliders = [
|
||||
$VBoxContainer/RotationalLimitsSection/rotLimitMinSlider,
|
||||
$VBoxContainer/RotationalLimitsSection/rotLimitMaxSlider
|
||||
]
|
||||
|
||||
for slider in limit_sliders:
|
||||
if slider:
|
||||
slider_manager.add_slider(slider)
|
||||
slider.value_changed.connect(_on_limit_slider_changed)
|
||||
|
||||
## Set up animation sliders
|
||||
func _setup_animation_sliders():
|
||||
var animation_sliders = [
|
||||
$VBoxContainer/AnimationSection/animFramesSlider,
|
||||
$VBoxContainer/AnimationSection/animSpeedSlider
|
||||
]
|
||||
|
||||
for slider in animation_sliders:
|
||||
if slider:
|
||||
slider_manager.add_slider(slider)
|
||||
slider.value_changed.connect(_on_animation_slider_changed)
|
||||
|
||||
## Set up opacity slider
|
||||
func _setup_opacity_slider():
|
||||
var opacity_slider = $VBoxContainer/RenderingSection/opacitySlider
|
||||
if opacity_slider:
|
||||
slider_manager.add_slider(opacity_slider)
|
||||
opacity_slider.value_changed.connect(_on_opacity_slider_changed)
|
||||
|
||||
## Generic slider change handlers
|
||||
func _on_drag_slider_value_changed(value: float):
|
||||
if Global.heldSprite:
|
||||
Global.heldSprite.dragSpeed = value
|
||||
|
||||
func _on_wobble_slider_changed(value: float):
|
||||
# Wobble sliders handle their own parameter updates
|
||||
# Just update the wobble sync control if needed
|
||||
if wobbleSyncControl:
|
||||
wobbleSyncControl.updateUI()
|
||||
|
||||
func _on_rotation_slider_changed(value: float):
|
||||
# Rotation sliders handle their own parameter updates
|
||||
if Global.heldSprite:
|
||||
changeRotLimit() # Update the rotation limit display
|
||||
|
||||
func _on_limit_slider_changed(value: float):
|
||||
# Limit sliders handle their own parameter updates
|
||||
if Global.heldSprite:
|
||||
changeRotLimit() # Update the rotation limit display
|
||||
|
||||
func _on_animation_slider_changed(value: float):
|
||||
# Animation sliders handle their own parameter updates
|
||||
if Global.heldSprite:
|
||||
# Special handling for frames slider
|
||||
var frames_slider = $VBoxContainer/AnimationSection/animFramesSlider
|
||||
if frames_slider and frames_slider.value != Global.heldSprite.frames:
|
||||
spriteSpin.hframes = Global.heldSprite.frames
|
||||
|
||||
func _on_opacity_slider_changed(value: float):
|
||||
# Opacity slider handles its own parameter updates via sprite_property
|
||||
pass
|
||||
|
||||
func setImage():
|
||||
if Global.heldSprite == null:
|
||||
@@ -32,57 +150,28 @@ func setImage():
|
||||
var displaySize = Global.heldSprite.imageData.get_size().y
|
||||
spriteRotDisplay.scale = Vector2(1,1) * (150.0/displaySize)
|
||||
|
||||
$Slider/Label.text = "drag: " + str(Global.heldSprite.dragSpeed)
|
||||
$Slider/DragSlider.value = Global.heldSprite.dragSpeed
|
||||
# Update all slider components with the current sprite
|
||||
if slider_manager:
|
||||
slider_manager.update_all_sliders(Global.heldSprite)
|
||||
|
||||
# Update individual sliders with current values
|
||||
_update_all_slider_values()
|
||||
|
||||
$WobbleControl/xFrqLabel.text = "x frequency: " + str(Global.heldSprite.xFrq)
|
||||
$WobbleControl/xAmpLabel.text = "x amplitude: " + str(Global.heldSprite.xAmp)
|
||||
$Position/fileTitle.text = Global.heldSprite.path.replace("user://", "")
|
||||
|
||||
$WobbleControl/xFrq.value = Global.heldSprite.xFrq
|
||||
$WobbleControl/xAmp.value = Global.heldSprite.xAmp
|
||||
|
||||
$WobbleControl/yFrqLabel.text = "y frequency: " + str(Global.heldSprite.yFrq)
|
||||
$WobbleControl/yAmpLabel.text = "y amplitude: " + str(Global.heldSprite.yAmp)
|
||||
|
||||
$WobbleControl/yFrq.value = Global.heldSprite.yFrq
|
||||
$WobbleControl/yAmp.value = Global.heldSprite.yAmp
|
||||
$VBoxContainer/PhysicsSection/BounceVelocity.button_pressed = Global.heldSprite.ignoreBounce
|
||||
$VBoxContainer/RenderingSection/ClipLinked.button_pressed = Global.heldSprite.clipped
|
||||
|
||||
$WobbleControl/rFrqLabel.text = "rotation frequency: " + str(Global.heldSprite.rFrq)
|
||||
$WobbleControl/rAmpLabel.text = "rotation percentage: " + str(Global.heldSprite.rAmp) + "%"
|
||||
|
||||
$WobbleControl/rFrq.value = Global.heldSprite.rFrq
|
||||
$WobbleControl/rAmp.value = Global.heldSprite.rAmp
|
||||
$VBoxContainer/Buttons/SpeakingWrapper/Speaking.frame = Global.heldSprite.showOnTalk
|
||||
$VBoxContainer/Buttons/BlinkingWrapper/Blinking.frame = Global.heldSprite.showOnBlink
|
||||
|
||||
$Rotation/rDragLabel.text = "rotational drag: " + str(Global.heldSprite.rdragStr)
|
||||
$Rotation/rDrag.value = Global.heldSprite.rdragStr
|
||||
$VBoxContainer/RenderingSection/affectChildrenCheck.button_pressed = Global.heldSprite.affectChildrenOpacity
|
||||
$VBoxContainer/RenderingSection/blendModeContainer/blendModeDropdown.selected = Global.heldSprite.blendMode
|
||||
|
||||
$Buttons/Speaking.frame = Global.heldSprite.showOnTalk
|
||||
$Buttons/Blinking.frame = Global.heldSprite.showOnBlink
|
||||
|
||||
$RotationalLimits/rotLimitMin.value = Global.heldSprite.rLimitMin
|
||||
$RotationalLimits/RotLimitMin.text = "rotational limit min: " + str(Global.heldSprite.rLimitMin)
|
||||
$RotationalLimits/rotLimitMax.value = Global.heldSprite.rLimitMax
|
||||
$RotationalLimits/RotLimitMax.text = "rotational limit max: " + str(Global.heldSprite.rLimitMax)
|
||||
|
||||
$Rotation/squashlabel.text = "squash: " + str(Global.heldSprite.stretchAmount)
|
||||
$Rotation/squash.value = Global.heldSprite.stretchAmount
|
||||
|
||||
$Position/fileTitle.text = Global.heldSprite.path
|
||||
|
||||
$Buttons/CheckBox.button_pressed = Global.heldSprite.ignoreBounce
|
||||
$Buttons/ClipLinked.button_pressed = Global.heldSprite.clipped
|
||||
|
||||
$Animation/animSpeedLabel.text = "animation speed: " + str(Global.heldSprite.animSpeed)
|
||||
$Animation/animSpeed.value = Global.heldSprite.animSpeed
|
||||
|
||||
$Animation/animFramesLabel.text = "sprite frames: " + str(Global.heldSprite.frames)
|
||||
$Animation/animFrames.value = Global.heldSprite.frames
|
||||
|
||||
$Opacity/opacityLabel.text = "opacity: " + str(int(Global.heldSprite.spriteOpacity * 100)) + "%"
|
||||
$Opacity/opacitySlider.value = Global.heldSprite.spriteOpacity
|
||||
$Opacity/affectChildrenCheck.button_pressed = Global.heldSprite.affectChildrenOpacity
|
||||
|
||||
$VisToggle/setToggle/Label.text = "toggle: \"" + Global.heldSprite.toggle + "\""
|
||||
$VBoxContainer/BoxContainer/setToggle/Label.text = "toggle: \"" + Global.heldSprite.toggle + "\""
|
||||
|
||||
changeRotLimit()
|
||||
|
||||
@@ -99,10 +188,10 @@ func setImage():
|
||||
updateWobbleControlStates()
|
||||
|
||||
if Global.heldSprite.parentId == null:
|
||||
$Buttons/Unlink.visible = false
|
||||
$VBoxContainer/Buttons/UnlinkWrapper/Unlink.visible = false
|
||||
parentSpin.visible = false
|
||||
else:
|
||||
$Buttons/Unlink.visible = true
|
||||
$VBoxContainer/Buttons/UnlinkWrapper/Unlink.visible = true
|
||||
|
||||
var nodes = get_tree().get_nodes_in_group(str(Global.heldSprite.parentId))
|
||||
|
||||
@@ -114,29 +203,91 @@ func setImage():
|
||||
parentSpin.hframes = nodes[0].frames
|
||||
parentSpin.visible = true
|
||||
|
||||
## Update all slider components with current sprite values
|
||||
func _update_all_slider_values():
|
||||
if not Global.heldSprite:
|
||||
return
|
||||
|
||||
# Update drag slider
|
||||
var drag_slider = $VBoxContainer/PhysicsSection/DragSlider
|
||||
if drag_slider:
|
||||
drag_slider.set_value(Global.heldSprite.dragSpeed)
|
||||
|
||||
# Update wobble sliders
|
||||
var wobble_sliders = {
|
||||
$VBoxContainer/WobbleSection/xFrqSlider: Global.heldSprite.xFrq,
|
||||
$VBoxContainer/WobbleSection/xAmpSlider: Global.heldSprite.xAmp,
|
||||
$VBoxContainer/WobbleSection/yFrqSlider: Global.heldSprite.yFrq,
|
||||
$VBoxContainer/WobbleSection/yAmpSlider: Global.heldSprite.yAmp,
|
||||
$VBoxContainer/WobbleSection/rFrqSlider: Global.heldSprite.rFrq,
|
||||
$VBoxContainer/WobbleSection/rAmpSlider: Global.heldSprite.rAmp
|
||||
}
|
||||
|
||||
for slider in wobble_sliders:
|
||||
if slider:
|
||||
slider.set_value(wobble_sliders[slider])
|
||||
|
||||
# Update rotation sliders
|
||||
var rotation_sliders = {
|
||||
$VBoxContainer/PhysicsSection/rDragSlider: Global.heldSprite.rdragStr,
|
||||
$VBoxContainer/PhysicsSection/squashSlider: Global.heldSprite.stretchAmount
|
||||
}
|
||||
|
||||
for slider in rotation_sliders:
|
||||
if slider:
|
||||
slider.set_value(rotation_sliders[slider])
|
||||
|
||||
# Update limit sliders
|
||||
var limit_sliders = {
|
||||
$VBoxContainer/RotationalLimitsSection/rotLimitMinSlider: Global.heldSprite.rLimitMin,
|
||||
$VBoxContainer/RotationalLimitsSection/rotLimitMaxSlider: Global.heldSprite.rLimitMax
|
||||
}
|
||||
|
||||
for slider in limit_sliders:
|
||||
if slider:
|
||||
slider.set_value(limit_sliders[slider])
|
||||
|
||||
# Update animation sliders
|
||||
var animation_sliders = {
|
||||
$VBoxContainer/AnimationSection/animFramesSlider: Global.heldSprite.frames,
|
||||
$VBoxContainer/AnimationSection/animSpeedSlider: Global.heldSprite.animSpeed
|
||||
}
|
||||
|
||||
for slider in animation_sliders:
|
||||
if slider:
|
||||
slider.set_value(animation_sliders[slider])
|
||||
|
||||
# Update opacity slider
|
||||
var opacity_slider = $VBoxContainer/RenderingSection/opacitySlider
|
||||
if opacity_slider:
|
||||
opacity_slider.set_value(Global.heldSprite.spriteOpacity)
|
||||
|
||||
## Update wobble control states based on sync status
|
||||
func updateWobbleControlStates():
|
||||
if Global.heldSprite == null:
|
||||
return
|
||||
|
||||
var isSynced = Global.heldSprite.isSynced()
|
||||
# Use slider manager if available
|
||||
if slider_manager:
|
||||
slider_manager.update_wobble_control_states(Global.heldSprite)
|
||||
|
||||
# Keep wobble controls enabled even when synced (they edit group settings)
|
||||
$WobbleControl/xFrq.editable = true
|
||||
$WobbleControl/xAmp.editable = true
|
||||
$WobbleControl/yFrq.editable = true
|
||||
$WobbleControl/yAmp.editable = true
|
||||
$WobbleControl/rFrq.editable = true
|
||||
$WobbleControl/rAmp.editable = true
|
||||
# Also update individual wobble sliders for visual feedback
|
||||
var is_synced = Global.heldSprite.isSynced()
|
||||
var tint = Color(1.0, 1.0, 0.9) if is_synced else Color.WHITE
|
||||
|
||||
# Visual feedback for sync state - slight tint to indicate group editing
|
||||
var tint = Color(1.0, 1.0, 0.9) if isSynced else Color.WHITE
|
||||
$WobbleControl/xFrq.modulate = tint
|
||||
$WobbleControl/xAmp.modulate = tint
|
||||
$WobbleControl/yFrq.modulate = tint
|
||||
$WobbleControl/yAmp.modulate = tint
|
||||
$WobbleControl/rFrq.modulate = tint
|
||||
$WobbleControl/rAmp.modulate = tint
|
||||
var wobble_sliders = [
|
||||
$VBoxContainer/WobbleSection/xFrqSlider,
|
||||
$VBoxContainer/WobbleSection/xAmpSlider,
|
||||
$VBoxContainer/WobbleSection/yFrqSlider,
|
||||
$VBoxContainer/WobbleSection/yAmpSlider,
|
||||
$VBoxContainer/WobbleSection/rFrqSlider,
|
||||
$VBoxContainer/WobbleSection/rAmpSlider
|
||||
]
|
||||
|
||||
for slider in wobble_sliders:
|
||||
if slider and slider.has_method("set_modulate_color"):
|
||||
slider.set_modulate_color(tint)
|
||||
slider.set_editable(true) # Keep enabled even when synced
|
||||
|
||||
func _process(delta):
|
||||
|
||||
@@ -150,9 +301,9 @@ func _process(delta):
|
||||
spriteSpin.rotate_y(delta*4.0)
|
||||
parentSpin.rotate_y(delta*4.0)
|
||||
|
||||
$Position/Label.text = "position X : "+str(obj.position.x)+" Y: " + str(obj.position.y)
|
||||
$Position/Label2.text = "offset X : "+str(obj.offset.x)+" Y: " + str(obj.offset.y)
|
||||
$Position/Label3.text = "layer : "+str(obj.z)
|
||||
$VBoxContainer/InfoSection/PositionLabel.text = "position X : "+str(obj.position.x)+" Y: " + str(obj.position.y)
|
||||
$VBoxContainer/InfoSection/OffsetLabel.text = "offset X : "+str(obj.offset.x)+" Y: " + str(obj.offset.y)
|
||||
$VBoxContainer/InfoSection/LayerLabel.text = "layer : "+str(obj.z)
|
||||
|
||||
#Sprite Rotational Limit Display
|
||||
|
||||
@@ -160,85 +311,22 @@ func _process(delta):
|
||||
var minimum = Global.heldSprite.rLimitMin
|
||||
|
||||
spriteRotDisplay.rotation_degrees = sin(Global.animationTick*0.05)*(size/2.0)+(minimum+(size/2.0))
|
||||
$RotationalLimits/RotBack/RotLineDisplay3.rotation_degrees = spriteRotDisplay.rotation_degrees
|
||||
|
||||
|
||||
func _on_drag_slider_value_changed(value):
|
||||
if Global.heldSprite != null:
|
||||
$Slider/Label.text = "drag: " + str(value)
|
||||
Global.heldSprite.dragSpeed = value
|
||||
|
||||
|
||||
func _on_x_frq_value_changed(value):
|
||||
print("DEBUG: _on_x_frq_value_changed called with value: ", value)
|
||||
$WobbleControl/xFrqLabel.text = "x frequency: " + str(value)
|
||||
Global.heldSprite.updateWobbleParameter("xFrq", value)
|
||||
# Refresh wobble sync control after parameter change
|
||||
if wobbleSyncControl:
|
||||
wobbleSyncControl.updateUI()
|
||||
|
||||
|
||||
func _on_x_amp_value_changed(value):
|
||||
print("DEBUG: _on_x_amp_value_changed called with value: ", value)
|
||||
$WobbleControl/xAmpLabel.text = "x amplitude: " + str(value)
|
||||
Global.heldSprite.updateWobbleParameter("xAmp", value)
|
||||
# Refresh wobble sync control after parameter change
|
||||
if wobbleSyncControl:
|
||||
wobbleSyncControl.updateUI()
|
||||
|
||||
|
||||
func _on_y_frq_value_changed(value):
|
||||
print("DEBUG: _on_y_frq_value_changed called with value: ", value)
|
||||
$WobbleControl/yFrqLabel.text = "y frequency: " + str(value)
|
||||
Global.heldSprite.updateWobbleParameter("yFrq", value)
|
||||
# Refresh wobble sync control after parameter change
|
||||
if wobbleSyncControl:
|
||||
wobbleSyncControl.updateUI()
|
||||
|
||||
func _on_y_amp_value_changed(value):
|
||||
print("DEBUG: _on_y_amp_value_changed called with value: ", value)
|
||||
$WobbleControl/yAmpLabel.text = "y amplitude: " + str(value)
|
||||
Global.heldSprite.updateWobbleParameter("yAmp", value)
|
||||
# Refresh wobble sync control after parameter change
|
||||
if wobbleSyncControl:
|
||||
wobbleSyncControl.updateUI()
|
||||
|
||||
|
||||
func _on_r_frq_value_changed(value):
|
||||
print("DEBUG: _on_r_frq_value_changed called with value: ", value)
|
||||
$WobbleControl/rFrqLabel.text = "rotation frequency: " + str(value)
|
||||
Global.heldSprite.updateWobbleParameter("rFrq", value)
|
||||
# Refresh wobble sync control after parameter change
|
||||
if wobbleSyncControl:
|
||||
wobbleSyncControl.updateUI()
|
||||
|
||||
func _on_r_amp_value_changed(value):
|
||||
print("DEBUG: _on_r_amp_value_changed called with value: ", value)
|
||||
$WobbleControl/rAmpLabel.text = "rotation amplitude: " + str(value) + "%"
|
||||
Global.heldSprite.updateWobbleParameter("rAmp", value)
|
||||
# Refresh wobble sync control after parameter change
|
||||
if wobbleSyncControl:
|
||||
wobbleSyncControl.updateUI()
|
||||
|
||||
|
||||
func _on_r_drag_value_changed(value):
|
||||
$Rotation/rDragLabel.text = "rotational drag: " + str(value)
|
||||
Global.heldSprite.rdragStr = value
|
||||
$VBoxContainer/RotationalLimitsSection/RotBack/RotLineDisplay3.rotation_degrees = spriteRotDisplay.rotation_degrees
|
||||
|
||||
|
||||
func _on_speaking_pressed():
|
||||
var f = $Buttons/Speaking.frame
|
||||
var f = $VBoxContainer/Buttons/SpeakingWrapper/Speaking.frame
|
||||
f = (f+1) % 3
|
||||
|
||||
$Buttons/Speaking.frame = f
|
||||
$VBoxContainer/Buttons/SpeakingWrapper/Speakingaking.frame = f
|
||||
Global.heldSprite.showOnTalk = f
|
||||
|
||||
|
||||
func _on_blinking_pressed():
|
||||
var f = $Buttons/Blinking.frame
|
||||
var f = $VBoxContainer/Buttons/BlinkingWrapper/Blinking.frame
|
||||
f = (f+1) % 3
|
||||
|
||||
$Buttons/Blinking.frame = f
|
||||
$VBoxContainer/Buttons/BlinkingWrapper/Blinking.frame = f
|
||||
Global.heldSprite.showOnBlink = f
|
||||
|
||||
|
||||
@@ -259,39 +347,34 @@ func _on_unlink_pressed():
|
||||
setImage()
|
||||
|
||||
|
||||
func _on_rot_limit_min_value_changed(value):
|
||||
$RotationalLimits/RotLimitMin.text = "rotational limit min: " + str(value)
|
||||
Global.heldSprite.rLimitMin = value
|
||||
|
||||
changeRotLimit()
|
||||
|
||||
func _on_rot_limit_max_value_changed(value):
|
||||
$RotationalLimits/RotLimitMax.text = "rotational limit max: " + str(value)
|
||||
Global.heldSprite.rLimitMax = value
|
||||
|
||||
changeRotLimit()
|
||||
|
||||
func changeRotLimit():
|
||||
$RotationalLimits/RotBack/rotLimitBar.value = Global.heldSprite.rLimitMax - Global.heldSprite.rLimitMin
|
||||
$RotationalLimits/RotBack/rotLimitBar.rotation_degrees = Global.heldSprite.rLimitMin + 90
|
||||
$VBoxContainer/RotationalLimitsSection/RotBack/rotLimitBar.value = Global.heldSprite.rLimitMax - Global.heldSprite.rLimitMin
|
||||
$VBoxContainer/RotationalLimitsSection/RotBack/rotLimitBar.rotation_degrees = Global.heldSprite.rLimitMin + 90
|
||||
|
||||
$VBoxContainer/RotationalLimitsSection/RotBack/RotLineDisplay.rotation_degrees = Global.heldSprite.rLimitMin
|
||||
$VBoxContainer/RotationalLimitsSection/RotBack/RotLineDisplay2.rotation_degrees = Global.heldSprite.rLimitMax
|
||||
|
||||
$RotationalLimits/RotBack/RotLineDisplay.rotation_degrees = Global.heldSprite.rLimitMin
|
||||
$RotationalLimits/RotBack/RotLineDisplay2.rotation_degrees = Global.heldSprite.rLimitMax
|
||||
|
||||
func setLayerButtons():
|
||||
if Global.heldSprite == null:
|
||||
return
|
||||
|
||||
var a = Global.heldSprite.costumeLayers.duplicate()
|
||||
|
||||
$Layers/Layer1.frame = 1-a[0]
|
||||
$Layers/Layer2.frame = 1-a[1]
|
||||
$Layers/Layer3.frame = 1-a[2]
|
||||
$Layers/Layer4.frame = 1-a[3]
|
||||
$Layers/Layer5.frame = 1-a[4]
|
||||
$Layers/Layer6.frame = 1-a[5]
|
||||
$Layers/Layer7.frame = 1-a[6]
|
||||
$Layers/Layer8.frame = 1-a[7]
|
||||
$Layers/Layer9.frame = 1-a[8]
|
||||
$Layers/Layer10.frame = 1-a[9]
|
||||
$VBoxContainer/LayersSection/HBoxContainer/Layer1Container/Layer1.frame = 1-a[0]
|
||||
$VBoxContainer/LayersSection/HBoxContainer/Layer2Container/Layer2.frame = 1-a[1]
|
||||
$VBoxContainer/LayersSection/HBoxContainer/Layer3Container/Layer3.frame = 1-a[2]
|
||||
$VBoxContainer/LayersSection/HBoxContainer/Layer4Container/Layer4.frame = 1-a[3]
|
||||
$VBoxContainer/LayersSection/HBoxContainer/Layer5Container/Layer5.frame = 1-a[4]
|
||||
$VBoxContainer/LayersSection/HBoxContainer2/Layer6Container/Layer6.frame = 1-a[5]
|
||||
$VBoxContainer/LayersSection/HBoxContainer2/Layer7Container/Layer7.frame = 1-a[6]
|
||||
$VBoxContainer/LayersSection/HBoxContainer2/Layer8Container/Layer8.frame = 1-a[7]
|
||||
$VBoxContainer/LayersSection/HBoxContainer2/Layer9Container/Layer9.frame = 1-a[8]
|
||||
$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")
|
||||
for sprite in nodes:
|
||||
if sprite.costumeLayers[Global.main.costume - 1] == 1:
|
||||
@@ -300,7 +383,45 @@ func setLayerButtons():
|
||||
else:
|
||||
sprite.visible = 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():
|
||||
@@ -381,71 +502,67 @@ func layerSelected():
|
||||
var newPos = Vector2.ZERO
|
||||
match Global.main.costume:
|
||||
1:
|
||||
newPos = $Layers/Layer1.position
|
||||
newPos = $VBoxContainer/LayersSection/HBoxContainer/Layer1Container/Layer1.position
|
||||
2:
|
||||
newPos = $Layers/Layer2.position
|
||||
newPos = $VBoxContainer/LayersSection/HBoxContainer/Layer2Container/Layer2.position
|
||||
3:
|
||||
newPos = $Layers/Layer3.position
|
||||
newPos = $VBoxContainer/LayersSection/HBoxContainer/Layer3Container/Layer3.position
|
||||
4:
|
||||
newPos = $Layers/Layer4.position
|
||||
newPos = $VBoxContainer/LayersSection/HBoxContainer/Layer4Container/Layer4.position
|
||||
5:
|
||||
newPos = $Layers/Layer5.position
|
||||
newPos = $VBoxContainer/LayersSection/HBoxContainer/Layer5Container/Layer5.position
|
||||
6:
|
||||
newPos = $Layers/Layer6.position
|
||||
newPos = $VBoxContainer/LayersSection/HBoxContainer2/Layer6Container/Layer6.position
|
||||
7:
|
||||
newPos = $Layers/Layer7.position
|
||||
newPos = $VBoxContainer/LayersSection/HBoxContainer2/Layer7Container/Layer7.position
|
||||
8:
|
||||
newPos = $Layers/Layer8.position
|
||||
newPos = $VBoxContainer/LayersSection/HBoxContainer2/Layer8Container/Layer8.position
|
||||
9:
|
||||
newPos = $Layers/Layer9.position
|
||||
newPos = $VBoxContainer/LayersSection/HBoxContainer2/Layer9Container/Layer9.position
|
||||
10:
|
||||
newPos = $Layers/Layer10.position
|
||||
$Layers/Select.position = newPos
|
||||
|
||||
|
||||
func _on_squash_value_changed(value):
|
||||
$Rotation/squashlabel.text = "squash: " + str(value)
|
||||
Global.heldSprite.stretchAmount = value
|
||||
|
||||
|
||||
func _on_check_box_toggled(button_pressed):
|
||||
Global.heldSprite.ignoreBounce = button_pressed
|
||||
|
||||
|
||||
func _on_anim_speed_value_changed(value):
|
||||
$Animation/animSpeedLabel.text = "animation speed: " + str(value)
|
||||
Global.heldSprite.animSpeed = value
|
||||
|
||||
func _on_anim_frames_value_changed(value):
|
||||
$Animation/animFramesLabel.text = "sprite frames: " + str(value)
|
||||
Global.heldSprite.frames = value
|
||||
spriteSpin.hframes = Global.heldSprite.frames
|
||||
Global.heldSprite.changeFrames()
|
||||
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):
|
||||
Global.heldSprite.setClip(button_pressed)
|
||||
|
||||
|
||||
func _on_check_box_toggled(button_pressed):
|
||||
Global.heldSprite.ignoreBounce = button_pressed
|
||||
|
||||
|
||||
func _on_delete_pressed():
|
||||
Global.heldSprite.toggle = "null"
|
||||
$VisToggle/setToggle/Label.text = "toggle: \"" + Global.heldSprite.toggle + "\""
|
||||
$VBoxContainer/BoxContainer/setToggle/Label.text = "toggle: \"" + Global.heldSprite.toggle + "\""
|
||||
Global.heldSprite.makeVis()
|
||||
|
||||
func _on_set_toggle_pressed():
|
||||
$VisToggle/setToggle/Label.text = "toggle: AWAITING INPUT"
|
||||
$VBoxContainer/BoxContainer/setToggle/Label.text = "toggle: AWAITING INPUT"
|
||||
await Global.main.fatfuckingballs
|
||||
|
||||
var keys = await Global.main.spriteVisToggles
|
||||
var key = keys[0]
|
||||
Global.heldSprite.toggle = key
|
||||
$VisToggle/setToggle/Label.text = "toggle: \"" + Global.heldSprite.toggle + "\""
|
||||
|
||||
func _on_opacity_slider_value_changed(value):
|
||||
$Opacity/opacityLabel.text = "opacity: " + str(int(value * 100)) + "%"
|
||||
Global.heldSprite.spriteOpacity = value
|
||||
Global.heldSprite.updateOpacity()
|
||||
$VBoxContainer/BoxContainer/setToggle/Label.text = "toggle: \"" + Global.heldSprite.toggle + "\""
|
||||
|
||||
func _on_affect_children_check_toggled(button_pressed):
|
||||
Global.heldSprite.affectChildrenOpacity = button_pressed
|
||||
Global.heldSprite.updateOpacity()
|
||||
|
||||
## Set up the blend mode dropdown with all available blend modes
|
||||
func setupBlendModeDropdown():
|
||||
blendModeDropdown.clear()
|
||||
var blendModeNames = BlendModeManager.get_blend_mode_names()
|
||||
for mode_name in blendModeNames:
|
||||
blendModeDropdown.add_item(mode_name)
|
||||
|
||||
## Handle blend mode dropdown selection
|
||||
func _on_blend_mode_dropdown_item_selected(index: int):
|
||||
if Global.heldSprite != null:
|
||||
Global.heldSprite.updateBlendMode(index)
|
||||
Global.pushUpdate("Blend mode set to: " + BlendModeManager.get_blend_mode_name(index))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
15017
ui_scenes/spriteEditMenu/sprite_viewer.tscn19865666400.tmp
Normal file
15017
ui_scenes/spriteEditMenu/sprite_viewer.tscn19865666400.tmp
Normal file
File diff suppressed because one or more lines are too long
@@ -55,11 +55,10 @@ func updateGroupDropdown():
|
||||
# Add existing groups
|
||||
if WobbleSyncManager:
|
||||
var groups = WobbleSyncManager.getGroupNames()
|
||||
print("DEBUG: updateGroupDropdown - available groups: ", groups)
|
||||
for group_name in groups:
|
||||
groupDropdown.add_item(group_name)
|
||||
else:
|
||||
print("DEBUG: updateGroupDropdown - WobbleSyncManager not available")
|
||||
pass # WobbleSyncManager not available
|
||||
|
||||
# Add "Create New..." option
|
||||
groupDropdown.add_separator()
|
||||
@@ -67,7 +66,6 @@ func updateGroupDropdown():
|
||||
|
||||
# Select current group if sprite is synced
|
||||
if currentSprite != null and currentSprite.wobbleSyncGroup != "":
|
||||
print("DEBUG: updateGroupDropdown - sprite has group '", currentSprite.wobbleSyncGroup, "'")
|
||||
var group_index = -1
|
||||
for i in range(groupDropdown.get_item_count()):
|
||||
if groupDropdown.get_item_text(i) == currentSprite.wobbleSyncGroup:
|
||||
@@ -75,13 +73,10 @@ func updateGroupDropdown():
|
||||
break
|
||||
|
||||
if group_index != -1:
|
||||
print("DEBUG: updateGroupDropdown - selecting group at index ", group_index)
|
||||
groupDropdown.selected = group_index
|
||||
else:
|
||||
print("DEBUG: updateGroupDropdown - group '", currentSprite.wobbleSyncGroup, "' not found in dropdown")
|
||||
groupDropdown.selected = 0 # Select "None"
|
||||
else:
|
||||
print("DEBUG: updateGroupDropdown - sprite has no group, selecting 'None'")
|
||||
groupDropdown.selected = 0 # Select "None"
|
||||
|
||||
updating_dropdown = false
|
||||
@@ -100,11 +95,9 @@ func updateSyncIndicator():
|
||||
|
||||
## Set the current sprite to manage
|
||||
func setSprite(sprite):
|
||||
print("DEBUG: WobbleSyncControl.setSprite called with sprite ID: ", sprite.id if sprite else "null")
|
||||
currentSprite = sprite
|
||||
if currentSprite:
|
||||
print("DEBUG: Current sprite wobbleSyncGroup: '", currentSprite.wobbleSyncGroup, "'")
|
||||
print("DEBUG: Current sprite isSynced(): ", currentSprite.isSynced())
|
||||
pass # Sprite is valid
|
||||
updateUI()
|
||||
|
||||
## Get available groups for the dropdown (excluding "None" and "Create New...")
|
||||
@@ -120,18 +113,15 @@ func _on_group_dropdown_selected(index: int):
|
||||
return
|
||||
|
||||
var selected_text = groupDropdown.get_item_text(index)
|
||||
print("DEBUG: Dropdown selected: '", selected_text, "' at index ", index)
|
||||
|
||||
match selected_text:
|
||||
"None":
|
||||
# Remove sprite from any group
|
||||
if currentSprite.wobbleSyncGroup != "" and WobbleSyncManager:
|
||||
print("DEBUG: Removing sprite ID ", currentSprite.id, " from group '", currentSprite.wobbleSyncGroup, "'")
|
||||
WobbleSyncManager.removeSpriteFromAllGroups(currentSprite.id)
|
||||
Global.pushUpdate("Removed sprite from wobble sync group")
|
||||
|
||||
"Create New...":
|
||||
print("DEBUG: User selected 'Create New...' from dropdown")
|
||||
# Show create group dialog
|
||||
groupNameInput.text = ""
|
||||
groupNameInput.placeholder_text = "Enter group name..."
|
||||
@@ -143,45 +133,39 @@ func _on_group_dropdown_selected(index: int):
|
||||
_:
|
||||
# Join selected group
|
||||
if WobbleSyncManager and WobbleSyncManager.hasGroup(selected_text):
|
||||
print("DEBUG: Adding sprite ID ", currentSprite.id, " to group '", selected_text, "'")
|
||||
WobbleSyncManager.addSpriteToGroup(currentSprite.id, selected_text)
|
||||
# Refresh sprite editor to show updated wobble values
|
||||
if Global.spriteEdit:
|
||||
Global.spriteEdit.setImage()
|
||||
else:
|
||||
print("DEBUG: Group '", selected_text, "' not found or WobbleSyncManager not available")
|
||||
|
||||
pass # Group doesn't exist
|
||||
|
||||
updateSyncIndicator()
|
||||
|
||||
## Handle create group dialog confirmation
|
||||
func _on_create_group_confirmed():
|
||||
var group_name = groupNameInput.text.strip_edges()
|
||||
print("DEBUG: Create group confirmed with name: '", group_name, "'")
|
||||
|
||||
if group_name == "":
|
||||
print("DEBUG: Group name is empty")
|
||||
Global.pushUpdate("Group name cannot be empty")
|
||||
return
|
||||
|
||||
if WobbleSyncManager and WobbleSyncManager.hasGroup(group_name):
|
||||
print("DEBUG: Group '", group_name, "' already exists")
|
||||
Global.pushUpdate("Group \"" + group_name + "\" already exists")
|
||||
return
|
||||
|
||||
# Create group with current sprite
|
||||
if currentSprite != null and WobbleSyncManager:
|
||||
print("DEBUG: Creating group '", group_name, "' with sprite ID ", currentSprite.id)
|
||||
if WobbleSyncManager.createGroup(group_name, currentSprite.id):
|
||||
print("DEBUG: Group created successfully")
|
||||
createGroupDialog.hide()
|
||||
updateUI()
|
||||
# Refresh sprite editor to show sync status
|
||||
if Global.spriteEdit:
|
||||
Global.spriteEdit.setImage()
|
||||
else:
|
||||
print("DEBUG: Failed to create group")
|
||||
pass # Failed to create group
|
||||
else:
|
||||
print("DEBUG: No current sprite to add to group")
|
||||
pass # No sprite or WobbleSyncManager not available
|
||||
|
||||
## Handle create group dialog cancellation
|
||||
func _on_create_group_cancelled():
|
||||
@@ -203,5 +187,4 @@ func canSync() -> bool:
|
||||
|
||||
## Force refresh the UI - useful after loading saves
|
||||
func forceRefresh():
|
||||
print("DEBUG: WobbleSyncControl.forceRefresh called")
|
||||
updateUI()
|
||||
|
||||
@@ -2,9 +2,9 @@ extends NinePatchRect
|
||||
|
||||
@onready var spritePreview = $SpritePreview/Sprite2D
|
||||
@onready var outline = $Selected
|
||||
|
||||
|
||||
@onready var fade = $Fade
|
||||
@onready var button = $Button
|
||||
@onready var label = $Label
|
||||
|
||||
var sprite = null
|
||||
var parent = null
|
||||
@@ -14,9 +14,23 @@ var indent = 0
|
||||
var childrenTags = []
|
||||
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():
|
||||
var count = spritePath.get_slice_count("/") - 1
|
||||
$Label.text = spritePath.get_slice("/",count)
|
||||
# Use displayName if available, otherwise fallback to path
|
||||
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
|
||||
|
||||
spritePreview.texture = sprite.sprite.texture
|
||||
@@ -25,6 +39,115 @@ func _ready():
|
||||
spritePreview.scale = Vector2(1,1) * (60.0/displaySize)
|
||||
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():
|
||||
for child in childrenTags:
|
||||
|
||||
Reference in New Issue
Block a user