-
-
Notifications
You must be signed in to change notification settings - Fork 473
Expand file tree
/
Copy pathImportTagDialog.gd
More file actions
275 lines (244 loc) · 10.5 KB
/
ImportTagDialog.gd
File metadata and controls
275 lines (244 loc) · 10.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
extends AcceptDialog
var from_project: Project
var create_new_tags := false
var frame: int
var tag_id: int
@onready var from_project_list: OptionButton = %ProjectList
@onready var create_tags: CheckButton = %CreateTags
@onready var animation_tags_list: ItemList = %TagList
func _ready() -> void:
# connect signals
from_project_list.item_selected.connect(_on_FromProject_changed)
animation_tags_list.item_selected.connect(_on_TagList_id_pressed)
animation_tags_list.empty_clicked.connect(_on_TagList_empty_clicked)
create_tags.toggled.connect(_on_CreateTags_toggled)
func refresh_list() -> void:
animation_tags_list.clear()
get_ok_button().disabled = true
for tag: AnimationTag in from_project.animation_tags:
var img := from_project.new_empty_image()
DrawingAlgos.blend_layers(
img, from_project.frames[tag.from - 1], Vector2i.ZERO, from_project
)
var tex := ImageTexture.create_from_image(img)
var tag_title := tag.name
if tag_title == "":
tag_title = "(Untitled)"
var idx = animation_tags_list.add_item(tag_title, tex)
animation_tags_list.set_item_custom_fg_color(idx, tag.color)
func _on_CreateTags_toggled(pressed: bool) -> void:
create_new_tags = pressed
func prepare_and_show(frame_no: int) -> void:
# Reset UI
frame = frame_no
from_project_list.clear()
if Global.projects.find(from_project) < 0:
from_project = Global.current_project
# Populate project list
for project in Global.projects:
from_project_list.add_item(project.name)
from_project_list.select(Global.projects.find(from_project))
# Populate tag list
refresh_list()
title = str("Import Tag (After Frame ", frame + 1, ")")
popup_centered_clamped()
func _on_FromProject_changed(id: int) -> void:
from_project = Global.projects[id]
refresh_list()
func _on_confirmed() -> void:
var tag: AnimationTag = from_project.animation_tags[tag_id]
var frames := []
for i in range(tag.from - 1, tag.to):
frames.append(i)
if create_new_tags:
add_animation(frames, frame, tag)
else:
add_animation(frames, frame)
func _on_TagList_id_pressed(id: int) -> void:
get_ok_button().disabled = false
tag_id = id
func _on_TagList_empty_clicked(_at_position: Vector2, _mouse_button_index: int) -> void:
animation_tags_list.deselect_all()
get_ok_button().disabled = true
## Gets frame indices of [member from_project] and dumps it in the current project.
func add_animation(indices: Array, destination: int, from_tag: AnimationTag = null) -> void:
var project: Project = Global.current_project
if from_project == project: ## If we are copying tags within project
Global.animation_timeline.copy_frames(indices, destination, true, from_tag)
return
var new_animation_tags := project.animation_tags.duplicate()
# Loop through the tags to create new classes for them, so that they won't be the same
# as project.animation_tags's classes. Needed for undo/redo to work properly.
for i in new_animation_tags.size():
new_animation_tags[i] = AnimationTag.new(
new_animation_tags[i].name,
new_animation_tags[i].color,
new_animation_tags[i].from,
new_animation_tags[i].to
)
var imported_frames: Array[Frame] = [] # The copied frames
# the indices of newly copied frames
var copied_indices: PackedInt32Array = range(
destination + 1, (destination + 1) + indices.size()
)
project.undo_redo.create_action("Import Tag")
# Step 1: calculate layers to generate
var layer_to_names := PackedStringArray() # names of currently existing layers
for l in project.layers:
layer_to_names.append(l.name)
# the goal of this section is to mark existing layers with their indices else with -1
var layer_from_to := {} # indices of layers from and to
for from in from_project.layers.size():
var to := -1
var pos := 0
for i in layer_to_names.count(from_project.layers[from].name):
pos = layer_to_names.find(from_project.layers[from].name, pos)
# if layer types don't match, the destination is invalid.
if project.layers[pos].get_layer_type() != from_project.layers[from].get_layer_type():
# Don't give up if there is another layer with the same name, check that one as well
pos += 1
continue
# if destination is already assigned to another layer, then don't use it here.
if pos in layer_from_to.values():
# Don't give up if there is another layer with the same name, check that one as well
pos += 1
continue
to = pos
break
layer_from_to[from] = to
# Step 2: generate required layers
var combined_copy := Array() # Makes calculations easy (contains preview of final layer order).
combined_copy.append_array(project.layers)
var added_layers := Array() # Array of layers
# Array of indices to add the respective layers (in added_layers) to
var added_idx := PackedInt32Array()
var added_cels := Array() # Array of an Array of cels (added in same order as their layer)
# Create destinations for layers that don't have one yet
if layer_from_to.values().count(-1) > 0:
# As it is extracted from a dictionary, so i assume the keys aren't sorted
var from_layers_size = layer_from_to.keys().duplicate(true)
from_layers_size.sort() # it's values should now be from (layer size - 1) to zero
for i in from_layers_size:
if layer_from_to[i] == -1:
var from_layer := from_project.layers[i]
var type = from_layer.get_layer_type()
var l: BaseLayer
match type:
Global.LayerTypes.PIXEL:
l = PixelLayer.new(project)
Global.LayerTypes.GROUP:
l = GroupLayer.new(project)
Global.LayerTypes.THREE_D:
l = Layer3D.new(project)
Global.LayerTypes.TILEMAP:
l = LayerTileMap.new(project, from_layer.tileset)
l.place_only_mode = from_layer.place_only_mode
l.tile_size = from_layer.tile_size
l.tile_shape = from_layer.tile_shape
l.tile_layout = from_layer.tile_layout
l.tile_offset_axis = from_layer.tile_offset_axis
Global.LayerTypes.AUDIO:
l = AudioLayer.new(project)
l.audio = from_layer.audio
if l == null: # Ignore copying this layer if it isn't supported
continue
var cels := []
for f in project.frames:
cels.append(l.new_empty_cel())
l.name = from_project.layers[i].name # this will set it to the required layer name
# Set an appropriate parent
var new_layer_idx = combined_copy.size()
layer_from_to[i] = new_layer_idx
var from_children = from_project.layers[i].get_children(false)
for from_child in from_children: # If this layer had children
var child_to_idx = layer_from_to[from_project.layers.find(from_child)]
var to_child = combined_copy[child_to_idx]
if to_child in added_layers: # if child was added recently
to_child.parent = l
combined_copy.insert(new_layer_idx, l)
added_layers.append(l) # layer is now added
added_idx.append(new_layer_idx) # at index new_layer_idx
added_cels.append(cels) # with cels
# Now initiate import
for f in indices:
var src_frame: Frame = from_project.frames[f]
var new_frame := Frame.new()
imported_frames.append(new_frame)
new_frame.duration = src_frame.duration
for to in combined_copy.size():
var new_cel: BaseCel
if to in layer_from_to.values(): # We have data to Import to this layer index
var from = layer_from_to.find_key(to)
# Cel we're copying from, the source
var src_cel: BaseCel = from_project.frames[f].cels[from]
new_cel = src_cel.duplicate_cel()
if src_cel is Cel3D:
new_cel.size_changed(project.size)
elif src_cel is CelTileMap:
var copied_content := src_cel.copy_content() as Array
var src_img: ImageExtended = copied_content[0]
var empty := project.new_empty_image()
var copy := ImageExtended.new()
copy.copy_from_custom(empty, project.is_indexed())
copy.blit_rect(src_img, Rect2(Vector2.ZERO, src_img.get_size()), Vector2.ZERO)
new_cel.set_content([copy, copied_content[1]])
new_cel.set_indexed_mode(project.is_indexed())
else:
# Add more types here if they have a copy_content() method.
if src_cel is PixelCel:
var src_img: ImageExtended = src_cel.copy_content()
var empty := project.new_empty_image()
var copy := ImageExtended.new()
copy.copy_from_custom(empty, project.is_indexed())
copy.blit_rect(
src_img, Rect2(Vector2.ZERO, src_img.get_size()), Vector2.ZERO
)
new_cel.set_content(copy)
new_cel.set_indexed_mode(project.is_indexed())
else:
new_cel = combined_copy[to].new_empty_cel()
new_frame.cels.append(new_cel)
for tag in new_animation_tags: # Loop through the tags to see if the frame is in one
if copied_indices[0] >= tag.from && copied_indices[0] <= tag.to:
tag.to += 1
elif copied_indices[0] < tag.from:
tag.from += 1
tag.to += 1
if from_tag:
new_animation_tags.append(
AnimationTag.new(
from_tag.name, from_tag.color, copied_indices[0] + 1, copied_indices[-1] + 1
)
)
project.undo_redo.add_undo_method(project.remove_frames.bind(copied_indices))
project.undo_redo.add_do_method(project.add_layers.bind(added_layers, added_idx, added_cels))
project.undo_redo.add_do_method(project.add_frames.bind(imported_frames, copied_indices))
project.undo_redo.add_undo_method(project.remove_layers.bind(added_idx))
# Note: temporarily set the selected cels to an empty array (needed for undo/redo)
project.undo_redo.add_do_property(Global.current_project, "selected_cels", [])
project.undo_redo.add_undo_property(Global.current_project, "selected_cels", [])
var all_new_cels := []
# Select all the new frames so that it is easier to move/offset collectively if user wants
# To ease animation workflow, new current frame is the first copied frame instead of the last
var range_start: int = copied_indices[-1]
var range_end: int = copied_indices[0]
var frame_diff_sign := signi(range_end - range_start)
if frame_diff_sign == 0:
frame_diff_sign = 1
for i in range(range_start, range_end + frame_diff_sign, frame_diff_sign):
for j in range(0, combined_copy.size()):
var frame_layer := [i, j]
if !all_new_cels.has(frame_layer):
all_new_cels.append(frame_layer)
project.undo_redo.add_do_property(Global.current_project, "selected_cels", all_new_cels)
project.undo_redo.add_undo_method(
project.change_cel.bind(project.current_frame, project.current_layer)
)
project.undo_redo.add_do_method(project.change_cel.bind(range_end))
project.undo_redo.add_do_property(project, "animation_tags", new_animation_tags)
project.undo_redo.add_undo_property(project, "animation_tags", project.animation_tags)
project.undo_redo.add_do_method(Global.undo_or_redo.bind(false))
project.undo_redo.add_undo_method(Global.undo_or_redo.bind(true))
project.undo_redo.commit_action()
func _on_close_requested() -> void:
hide()