1
0
Fork 0
mirror of https://github.com/Orama-Interactive/Pixelorama.git synced 2025-01-18 09:09:47 +00:00

Basic Layer Groups and Timeline Refactor (#698)

* Fixed issues with Shading tool Saturation and Value not always being right in Hue Shading mode

* Shading tool hue shifting fixes and tweaks

* Bringing over changes from layer groups brach, without any changes to layer blending

* Some quick fixes to make it work again

* Fixed some of the places where GroupLayers cause errors. Cel Buttons are now hidden when groups are collapsed

* Layer drag highlighting (need to actually drop them correctly, also need to do cels)

* Added more layer hierarchy related functions, organized the function order in the Layer classes a bit

* Switched the layer type changing from string to int

* Moved layer type enum to Global

* Added get_layer_type_name(), currently used for the default layer name

* Renamed the layer get_children/is_a_parent_of functions

* changed get_layer_type_name() to get_default_name(number)

* New layer drag and dropping behavior

* Added read/write_image_data_from/to_pxo functions to Cel classes to handle saving/loading the binary image data for each cel type

* Fixed warning

* Added a line to child layers wich makes it easier to see where they are in the hierarchy

* Fixed debugger warning

* Fixed all cel types loading as PixelCels

* Fixed spacing issue with cels when collapsing groups

* Fixed bug when dropping a child layer to the bottom region of its parent group, where it would end up to far down (maybe disappearing)

* updated temporary todo comments

* Created a base scene for layer buttons and merged layer button script into one

* Prevent the case of parenting to itself in layer drag and drop, fixed static reference to LayerButton still being BaseLayerButton

* Use a base scene for CelButtons

* First bit of the refactoring work

* Several bits of refactoring

* Fixed moving cels

* Cleaned up Project.move_cel function

* Fixed project_layer_removed

* Updated change_frame_order on FrameButton. Some (not all) work on getting the layer UI updated when pressing buttons such as collapse/visible/lock

* Bug fixes. Updating layer button's buttons

* Fixed timeline selection issues when creating a new project. Some code cleanup

* tweaks

* Removed a bunch of commented out code

* Removing more commented out code

* Fixed bugs with timeline selectio. Fixed cels being placed in the reverse layer order when adding a frame

* Changed add/remove_frame to add/remove_frames (multiple support)

* Refactored copy_frames in animation timeline

* added copy function to cel classes

* added layer copy function

* simplifed copy_frames a tiny bit

* Updated TODO comments to categorize them and remove any that were already done

* Turned Project.add/remove_layer into Project.add/remove_layers (multiple support), not yet tested

* Seperated the layer cloning functionality in timeline's add_layer to its own function, since they're only used by one button, renamed to _on_Button_pressed naming scheme, added children support to the delete layer button

* some TODOs

* Added layer swapping

* Added priorities to refactor TODOs

* Simplified layer swapping code a little

* Fixed performance regression on changing project, updated TODOs

* Included _on_MergeDownLayer_pressed in timeline refactor

* Cleaned up _on_MergeDownLayer_pressed refactor

* If all frames are selected, prevent being able to remove all of them

* Fixed cel linking when cloning layers/frames. Moved the copy function from cel classes to layer classes, splitting into copy_cel and copy_all_cels

* Combined and rewrote the 2 project _toggle_layer_buttons_.. functions into 1 simpler _toggle_layer_buttons function

* Simplified _toggle_layer_buttons some more

* Added hierarchy support for move up/down layer buttons

* Added toggle_frame_buttons method to project (extracted from  _frame_changed). Called from main when setting up startup project. Removed _ from start of _toggle_layer_buttons name

* Fixed duplicate_layers parent references being to the original layers

* cleaned up project.move_layers method a bit

* TODOs

* moved the transform_content_confirm calls for the layer buttons in AnimationTimeline (Add/remove/clone) to the project layer modification functions

* animation first/last_frame tweaks and un-press play buttons when the first/last_frame are the same in _on_AnimationTimer_timeout in AnimationTimeline

* Cleaned up project_changed in ANimationTimeline a bit

* Cleaned up project_layer_added in AnimationTimeline

* Changed Layer classes get_default_name to set_name_to_default

* Cleaned up LayerButton.drop_data slightly

* Looked at some of my TODOs

* cleaned up copying cels

* Fixed CelButton linked_indicator not showing up right away when becoming linked

* Cleand up link/unlink cel menu option a little. Fixed situatoin where trying to call button_setup on cel_button that doesn't exist anymore due to undo/redo

* Fixed regression with copy_cel (linked) in when cloning a frame

* Minor cleanup, more detailed comments, updated TODOs

* more improved comments

* Made focus_mode on Cel/Layer/FrameButton NONE to fix bug where it looks like one is selected after pressing it and adding a new Layer/Frame (but its just in the focus state, not the pressed state

* Made AnimationTimeline.change_layer_order work a little more consistantly with LayerButton.drop_data, and fixed a minor bug in it

* Updated comments and TODOs

* cleanup

* removed some code that should no longer be needed

* updated comment

* removed Project's frames and layers setters _frames_changed and _layers_changed

* Made some 'for x in range(array.size())' just 'for x in array.size()'

* updated comments/TODOs

* Cel content changes intial

* Added 'content' methods to Cel classes

* Removed image var from PixelCelButton

* Reusing PixelCelButton.gd on GroupCelButton scene

* Renamed PixelCelButton.gd to CelButton.gd (as it will be used for all Cel Buttons) and deleted GroupCelButton.gd

* Hide the TransparentChecker on GroupCelButton.tscn until a preview texture is added for GroupCels

* TODOs, prevent memory leak when closing projects

* Link/unlink cel cleanup
:

* Added _project param to _init methods of Layer classes

* Added update_texture method to Cel classes (moving part from the update_texture and update_selected_cels_textures methods from Canvas.gd

* Removed a temporary check (which also fixed another bug)

* Clone child layers when cloning a layer

* Added temp dummy get_image method to GroupCel, and use get_image when copying or picking colors

* TODOs

* Made open_image_as_spritesheet_layer work after the timeline refactor (still doesn't work with groups yet though). TODO comment updates

* Added create_new_cel methods to Layer classes

* Updated TODOs and comments

* Renamed Layer class's create_empty_cel to new_empty_cel to match Project's new_emtpy_frame

* Renamed create_layer/cel_button to instantiate_layer/cel_button

* updated TODOs

* prioritized TODOs

* Fixed some warnings

* removed commented out code from previous commit

* Fixed export

* Made open_image_as_new_frame work after timeline refactor

* Fixed open_image_as_new_layer after timeline refactor

* Some linked cel fixes

* More linked cels fixes

* cleanup

* Optimized importing spreadsheet as new layer

* Fixed Scale Image crash with Groups

* Fixed onion skin with groups

* Removed blend_mode from BaseLayer for now

* Mostly fixed image effects

* Fixed resize canvas

* Fixed drag and drop not working with Cel Buttons on Group Layers

* updated TODOs

* Renamed Replace Frame (in open image) to Replace Cel

* Continued renaming Replace Frame to Replace Cel

* Made open_image_at_cels work after timeline refactor

* Added get_layer_path method to BaseLayer

* Replaced AtLayerSpinbox with AtLayerOption for Open Image as New Frame or Replace Cel

* Updated TODOs

* updated TODOs

* Comments for cel content methods

* fixed right clicking group cel button deselecting the button (even though cel is still selected

* frame/layer modification methods comments

* Removed unneeded size flags

* TODO updates

* Removed a loop that would never run from open_image_as_spritesheet_tab

* TODO update

* Combined BaseLayer.get_children_direct and get_children_recursive into a single get_children method with a bool for recursive. Added a get_child_count method

* Removed unneeded frame paramaters from _on_DeleteFrame_pressed and _on_CopyFrame_pressed

* TODO Updates

* Removed unneeded code from delete_frames

* Made delete_frames variable names more consistent with my other changes

* Continuation

* made variable names in copy_frames more consistent with rest of changes

* Update TODOs

* Removed TODOs for after this PR (moved to my notes)

* Fixed crash when pasting image on Group

* Fixed layer .visible check to be is_visible_in_hierarchy()

* Removed some drag highlight polish code that didn't work

* Removed code from Canvas update_texture and update_selected_cels_textures that was redundant

* gdformat

* gdformat

* gdlint fixes

* Fixed Cel button not having its linked indicator show when enabling new cels linked on a layer other than the current layer

* Fixed crop image and centralize image

* Added '# gdlint: ignore=max-public-methods' to the top of Project'

* Fixed dragging cels to layer of different type crash

* Formatted CelButton.gd

Co-authored-by: MrTriPie <MrTriPie>
This commit is contained in:
mrtripie 2022-09-28 14:59:49 -04:00 committed by GitHub
parent 5914471149
commit 1fa34d7196
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
48 changed files with 2154 additions and 1158 deletions

View file

@ -316,7 +316,7 @@ msgstr ""
msgid "New frame"
msgstr ""
msgid "Replace frame"
msgid "Replace cel"
msgstr ""
msgid "New layer"
@ -1553,12 +1553,18 @@ msgstr ""
msgid "Layer"
msgstr ""
msgid "Group"
msgstr ""
msgid "Layers"
msgstr ""
msgid "Create a new layer"
msgstr ""
msgid "Create a new group layer"
msgstr ""
msgid "Remove current layer"
msgstr ""
@ -1593,6 +1599,9 @@ msgid "Enable/disable cel linking\n\n"
"Linked cels are being shared across multiple frames"
msgstr ""
msgid "Expand/collapse group"
msgstr ""
msgid "Palette"
msgstr ""

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 B

View file

@ -0,0 +1,35 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/group_collapsed.png-9d08fac1c2f635c754860111d024aa0f.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/graphics/layers/group_collapsed.png"
dest_files=[ "res://.import/group_collapsed.png-9d08fac1c2f635c754860111d024aa0f.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=false
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
process/normal_map_invert_y=false
stream=false
size_limit=0
detect_3d=false
svg/scale=1.0

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

View file

@ -0,0 +1,35 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/group_expanded.png-f3cd620185a4989737d6d9a36f4b57f3.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/graphics/layers/group_expanded.png"
dest_files=[ "res://.import/group_expanded.png-f3cd620185a4989737d6d9a36f4b57f3.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=false
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
process/normal_map_invert_y=false
stream=false
size_limit=0
detect_3d=false
svg/scale=1.0

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 B

View file

@ -0,0 +1,35 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/group_new.png-4ebdc7dd84d8c8a7b7979f50f4471543.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/graphics/layers/group_new.png"
dest_files=[ "res://.import/group_new.png-4ebdc7dd84d8c8a7b7979f50f4471543.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=false
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
process/normal_map_invert_y=false
stream=false
size_limit=0
detect_3d=false
svg/scale=1.0

View file

@ -14,6 +14,16 @@ _global_script_classes=[ {
"language": "GDScript",
"path": "res://src/Classes/AnimationTag.gd"
}, {
"base": "Reference",
"class": "BaseCel",
"language": "GDScript",
"path": "res://src/Classes/BaseCel.gd"
}, {
"base": "Reference",
"class": "BaseLayer",
"language": "GDScript",
"path": "res://src/Classes/BaseLayer.gd"
}, {
"base": "VBoxContainer",
"class": "BaseTool",
"language": "GDScript",
@ -30,11 +40,6 @@ _global_script_classes=[ {
"path": "res://src/UI/Canvas/Canvas.gd"
}, {
"base": "Reference",
"class": "Cel",
"language": "GDScript",
"path": "res://src/Classes/Cel.gd"
}, {
"base": "Reference",
"class": "Drawer",
"language": "GDScript",
"path": "res://src/Classes/Drawers.gd"
@ -49,6 +54,16 @@ _global_script_classes=[ {
"language": "GDScript",
"path": "res://src/UI/Nodes/GradientEdit.gd"
}, {
"base": "BaseCel",
"class": "GroupCel",
"language": "GDScript",
"path": "res://src/Classes/GroupCel.gd"
}, {
"base": "BaseLayer",
"class": "GroupLayer",
"language": "GDScript",
"path": "res://src/Classes/GroupLayer.gd"
}, {
"base": "Line2D",
"class": "Guide",
"language": "GDScript",
@ -59,11 +74,6 @@ _global_script_classes=[ {
"language": "GDScript",
"path": "res://src/Classes/ImageEffect.gd"
}, {
"base": "Reference",
"class": "Layer",
"language": "GDScript",
"path": "res://src/Classes/Layer.gd"
}, {
"base": "Button",
"class": "LayerButton",
"language": "GDScript",
@ -99,6 +109,16 @@ _global_script_classes=[ {
"language": "GDScript",
"path": "res://src/UI/PatternsPopup.gd"
}, {
"base": "BaseCel",
"class": "PixelCel",
"language": "GDScript",
"path": "res://src/Classes/PixelCel.gd"
}, {
"base": "BaseLayer",
"class": "PixelLayer",
"language": "GDScript",
"path": "res://src/Classes/PixelLayer.gd"
}, {
"base": "Reference",
"class": "Project",
"language": "GDScript",
@ -136,16 +156,18 @@ _global_script_classes=[ {
} ]
_global_script_class_icons={
"AnimationTag": "",
"BaseCel": "",
"BaseLayer": "",
"BaseTool": "",
"Brushes": "",
"Canvas": "",
"Cel": "",
"Drawer": "",
"Frame": "",
"GradientEditNode": "",
"GroupCel": "",
"GroupLayer": "",
"Guide": "",
"ImageEffect": "",
"Layer": "",
"LayerButton": "",
"Palette": "",
"PaletteColor": "",
@ -153,6 +175,8 @@ _global_script_class_icons={
"PalettePanel": "",
"PaletteSwatch": "",
"Patterns": "",
"PixelCel": "",
"PixelLayer": "",
"Project": "",
"SelectionMap": "",
"SelectionTool": "",

View file

@ -423,6 +423,8 @@ func scale_image(width: int, height: int, interpolation: int) -> void:
for f in Global.current_project.frames:
for i in range(f.cels.size() - 1, -1, -1):
if f.cels[i] is GroupCel:
continue
var sprite := Image.new()
sprite.copy_from(f.cels[i].image)
# Different method for scale_3x
@ -449,6 +451,8 @@ func centralize() -> void:
# Find used rect of the current frame (across all of the layers)
var used_rect := Rect2()
for cel in Global.current_project.frames[Global.current_project.current_frame].cels:
if not cel is PixelCel:
continue
var cel_rect: Rect2 = cel.image.get_used_rect()
if not cel_rect.has_no_area():
used_rect = cel_rect if used_rect.has_no_area() else used_rect.merge(cel_rect)
@ -457,14 +461,16 @@ func centralize() -> void:
var offset: Vector2 = (0.5 * (Global.current_project.size - used_rect.size)).floor()
general_do_centralize()
for c in Global.current_project.frames[Global.current_project.current_frame].cels:
for cel in Global.current_project.frames[Global.current_project.current_frame].cels:
if not cel is PixelCel:
continue
var sprite := Image.new()
sprite.create(
Global.current_project.size.x, Global.current_project.size.y, false, Image.FORMAT_RGBA8
)
sprite.blend_rect(c.image, used_rect, offset)
Global.current_project.undo_redo.add_do_property(c.image, "data", sprite.data)
Global.current_project.undo_redo.add_undo_property(c.image, "data", c.image.data)
sprite.blend_rect(cel.image, used_rect, offset)
Global.current_project.undo_redo.add_do_property(cel.image, "data", sprite.data)
Global.current_project.undo_redo.add_undo_property(cel.image, "data", cel.image.data)
general_undo_centralize()
@ -473,6 +479,8 @@ func crop_image() -> void:
var used_rect := Rect2()
for f in Global.current_project.frames:
for cel in f.cels:
if not cel is PixelCel:
continue
cel.image.unlock() # May be unneeded now, but keep it just in case
var cel_used_rect: Rect2 = cel.image.get_used_rect()
if cel_used_rect == Rect2(0, 0, 0, 0): # If the cel has no content
@ -493,6 +501,8 @@ func crop_image() -> void:
# Loop through all the cels to crop them
for f in Global.current_project.frames:
for cel in f.cels:
if not cel is PixelCel:
continue
var sprite: Image = cel.image.get_rect(used_rect)
Global.current_project.undo_redo.add_do_property(cel.image, "data", sprite.data)
Global.current_project.undo_redo.add_undo_property(cel.image, "data", cel.image.data)
@ -504,6 +514,8 @@ func resize_canvas(width: int, height: int, offset_x: int, offset_y: int) -> voi
general_do_scale(width, height)
for f in Global.current_project.frames:
for c in f.cels:
if not c is PixelCel:
continue
var sprite := Image.new()
sprite.create(width, height, false, Image.FORMAT_RGBA8)
sprite.blend_rect(

View file

@ -380,7 +380,7 @@ func blend_layers(image: Image, frame: Frame, origin: Vector2 = Vector2(0, 0)) -
image.lock()
var layer_i := 0
for cel in frame.cels:
if Global.current_project.layers[layer_i].visible:
if Global.current_project.layers[layer_i].is_visible_in_hierarchy() and cel is PixelCel:
var cel_image := Image.new()
cel_image.copy_from(cel.image)
cel_image.lock()
@ -406,9 +406,12 @@ func blend_selected_cels(image: Image, frame: Frame, origin: Vector2 = Vector2(0
var test_array = [Global.current_project.current_frame, cel_ind]
if not test_array in Global.current_project.selected_cels:
continue
if not frame.cels[cel_ind] is PixelCel:
continue
var cel: Cel = frame.cels[cel_ind]
if Global.current_project.layers[layer_i].visible:
var cel: PixelCel = frame.cels[cel_ind]
if Global.current_project.layers[layer_i].is_visible_in_hierarchy():
var cel_image := Image.new()
cel_image.copy_from(cel.image)
cel_image.lock()

View file

@ -2,6 +2,7 @@ extends Node
signal project_changed
enum LayerTypes { PIXEL, GROUP }
enum GridTypes { CARTESIAN, ISOMETRIC, ALL }
enum PressureSensitivity { NONE, ALPHA, SIZE, ALPHA_AND_SIZE }
enum ColorFrom { THEME, CUSTOM }
@ -59,7 +60,6 @@ var current_project_index := 0 setget _project_changed
var ui_tooltips := {}
# Canvas related stuff
var layers_changed_skip := false
var can_draw := false
var move_guides_on_canvas := false
var has_focus := false
@ -144,6 +144,10 @@ var palettes := {}
# Nodes
var notification_label_node: PackedScene = preload("res://src/UI/NotificationLabel.tscn")
var pixel_layer_button_node: PackedScene = preload("res://src/UI/Timeline/PixelLayerButton.tscn")
var group_layer_button_node: PackedScene = preload("res://src/UI/Timeline/GroupLayerButton.tscn")
var pixel_cel_button_node: PackedScene = preload("res://src/UI/Timeline/PixelCelButton.tscn")
var group_cel_button_node: PackedScene = preload("res://src/UI/Timeline/GroupCelButton.tscn")
onready var control: Node = get_tree().current_scene
@ -449,24 +453,14 @@ func undo_or_redo(
if action_name == "Scale":
for i in project.frames.size():
for j in project.layers.size():
var current_cel: Cel = project.frames[i].cels[j]
current_cel.image_texture.create_from_image(current_cel.image, 0)
var current_cel: BaseCel = project.frames[i].cels[j]
current_cel.image_texture.create_from_image(current_cel.get_image(), 0)
canvas.camera_zoom()
canvas.grid.update()
canvas.pixel_grid.update()
project.selection_map_changed()
cursor_position_label.text = "[%s×%s]" % [project.size.x, project.size.y]
elif "Frame" in action_name:
# This actually means that frames.size is one, but it hasn't been updated yet
if (undo and project.frames.size() == 2) or project.frames.size() == 1: # Stop animating
play_forward.pressed = false
play_backwards.pressed = false
animation_timer.stop()
elif "Move Cels" == action_name:
project.frames = project.frames # to call frames_changed
canvas.update()
if !project.has_changed:
project.has_changed = true

View file

@ -110,11 +110,7 @@ func open_pxo_file(path: String, untitled_backup: bool = false, replace_empty: b
new_project.deserialize(dict.result)
for frame in new_project.frames:
for cel in frame.cels:
var buffer := file.get_buffer(new_project.size.x * new_project.size.y * 4)
cel.image.create_from_data(
new_project.size.x, new_project.size.y, false, Image.FORMAT_RGBA8, buffer
)
cel.image = cel.image # Just to call image_changed
cel.load_image_data_from_pxo(file, new_project.size)
if dict.result.has("brushes"):
for brush in dict.result.brushes:
@ -138,14 +134,13 @@ func open_pxo_file(path: String, untitled_backup: bool = false, replace_empty: b
new_project.tiles.reset_mask()
file.close()
if !empty_project:
Global.projects.append(new_project)
Global.tabs.current_tab = Global.tabs.get_tab_count() - 1
else:
if empty_project:
if dict.error == OK and dict.result.has("fps"):
Global.animation_timeline.fps_spinbox.value = dict.result.fps
new_project.frames = new_project.frames # Just to call frames_changed
new_project.layers = new_project.layers # Just to call layers_changed
Global.animation_timeline.project_changed()
else:
Global.projects.append(new_project)
Global.tabs.current_tab = Global.tabs.get_tab_count() - 1
Global.canvas.camera_zoom()
if not untitled_backup:
@ -200,13 +195,16 @@ func open_old_pxo_file(file: File, new_project: Project, first_line: String) ->
if file_major_version >= 0 and file_minor_version > 6:
var global_layer_line := file.get_line()
while global_layer_line == ".":
var layer_name := file.get_line()
var layer_visibility := file.get_8()
var layer_lock := file.get_8()
var layer_new_cels_linked := file.get_8()
var layer_dict := {
"name": file.get_line(),
"visible": file.get_8(),
"locked": file.get_8(),
"new_cels_linked": file.get_8(),
"linked_cels": []
}
linked_cels.append(file.get_var())
var l := Layer.new(layer_name, layer_visibility, layer_lock, layer_new_cels_linked, [])
var l := PixelLayer.new(new_project)
l.deserialize(layer_dict)
new_project.layers.append(l)
global_layer_line = file.get_line()
@ -223,17 +221,17 @@ func open_old_pxo_file(file: File, new_project: Project, first_line: String) ->
if file_major_version == 0 and file_minor_version < 7:
var layer_name_old_version = file.get_line()
if frame == 0:
var l := Layer.new(layer_name_old_version)
var l := PixelLayer.new(new_project, layer_name_old_version)
new_project.layers.append(l)
var cel_opacity := 1.0
if file_major_version >= 0 and file_minor_version > 5:
cel_opacity = file.get_float()
var image := Image.new()
image.create_from_data(width, height, false, Image.FORMAT_RGBA8, buffer)
frame_class.cels.append(Cel.new(image, cel_opacity))
frame_class.cels.append(PixelCel.new(image, cel_opacity))
if file_major_version >= 0 and file_minor_version >= 7:
if frame in linked_cels[layer_i]:
var linked_cel: Cel = new_project.layers[layer_i].linked_cels[0].cels[layer_i]
var linked_cel: PixelCel = new_project.layers[layer_i].linked_cels[0].cels[layer_i]
new_project.layers[layer_i].linked_cels.append(frame_class)
frame_class.cels[layer_i].image = linked_cel.image
frame_class.cels[layer_i].image_texture = linked_cel.image_texture
@ -358,7 +356,7 @@ func save_pxo_file(
file.store_line(to_save)
for frame in project.frames:
for cel in frame.cels:
file.store_buffer(cel.image.get_data())
cel.save_image_data_to_pxo(file)
for brush in project.brushes:
file.store_buffer(brush.get_data())
@ -401,12 +399,12 @@ func save_pxo_file(
func open_image_as_new_tab(path: String, image: Image) -> void:
var project = Project.new([], path.get_file(), image.get_size())
project.layers.append(Layer.new())
project.layers.append(PixelLayer.new(project))
Global.projects.append(project)
var frame := Frame.new()
image.convert(Image.FORMAT_RGBA8)
frame.cels.append(Cel.new(image, 1))
frame.cels.append(PixelCel.new(image, 1))
project.frames.append(frame)
set_new_imported_tab(project, path)
@ -414,7 +412,7 @@ func open_image_as_new_tab(path: String, image: Image) -> void:
func open_image_as_spritesheet_tab(path: String, image: Image, horiz: int, vert: int) -> void:
var project = Project.new([], path.get_file())
project.layers.append(Layer.new())
project.layers.append(PixelLayer.new(project))
Global.projects.append(project)
horiz = min(horiz, image.get_size().x)
vert = min(vert, image.get_size().y)
@ -429,16 +427,8 @@ func open_image_as_spritesheet_tab(path: String, image: Image, horiz: int, vert:
)
project.size = cropped_image.get_size()
cropped_image.convert(Image.FORMAT_RGBA8)
frame.cels.append(Cel.new(cropped_image, 1))
for _i in range(1, project.layers.size()):
var empty_sprite := Image.new()
empty_sprite.create(project.size.x, project.size.y, false, Image.FORMAT_RGBA8)
empty_sprite.fill(Color(0, 0, 0, 0))
frame.cels.append(Cel.new(empty_sprite, 1))
frame.cels.append(PixelCel.new(cropped_image, 1))
project.frames.append(frame)
set_new_imported_tab(project, path)
@ -461,95 +451,84 @@ func open_image_as_spritesheet_layer(
# Initialize undo mechanism
project.undos += 1
project.undo_redo.create_action("Add Spritesheet Layer")
var new_layers: Array = project.layers.duplicate()
var new_frames: Array = []
# Create a duplicate of "project.frames"
for i in project.frames.size():
var frame := Frame.new()
frame.cels = project.frames[i].cels.duplicate(true)
new_frames.append(frame)
var new_layers: Array = project.layers.duplicate() # Used for updating linked_cels lists
# Create new frames (if needed)
var new_frames_size = start_frame + (vertical * horizontal)
var new_frames_size = max(project.frames.size(), start_frame + (vertical * horizontal))
var frames := []
var frame_indices: Array
if new_frames_size > project.frames.size():
var required_frames = new_frames_size - project.frames.size()
frame_indices = range(
project.current_frame + 1, project.current_frame + required_frames + 1
)
for i in required_frames:
var new_frame := Frame.new()
for l_i in range(new_layers.size()): # Create as many cels as there are layers
var new_img := Image.new()
new_img.create(project_width, project_height, false, Image.FORMAT_RGBA8)
new_frame.cels.append(Cel.new(new_img, 1))
if new_layers[l_i].new_cels_linked:
for l_i in range(project.layers.size()): # Create as many cels as there are layers
new_frame.cels.append(project.layers[l_i].new_empty_cel())
if new_layers[l_i].get("new_cels_linked"):
new_layers[l_i].linked_cels.append(new_frame)
new_frame.cels[l_i].image = new_layers[l_i].linked_cels[0].cels[l_i].image
new_frame.cels[l_i].set_content(
new_layers[l_i].linked_cels[0].cels[l_i].get_content()
)
new_frame.cels[l_i].image_texture = new_layers[l_i].linked_cels[0].cels[l_i].image_texture
new_frames.insert(project.current_frame + 1, new_frame)
frames.append(new_frame)
# Create new layer for spritesheet
var layer := Layer.new(file_name)
new_layers.append(layer)
for f in new_frames:
var new_layer := Image.new()
new_layer.create(project.size.x, project.size.y, false, Image.FORMAT_RGBA8)
f.cels.append(Cel.new(new_layer, 1))
# Slice spritesheet
var image_no: int = 0
var layer_index = new_layers.size() - 1
for yy in range(vertical):
for xx in range(horizontal):
var layer := PixelLayer.new(project, file_name)
var cels := []
for f in new_frames_size:
if f >= start_frame and f < (start_frame + (vertical * horizontal)):
# Slice spritesheet
var xx: int = (f - start_frame) % horizontal
var yy: int = (f - start_frame) / horizontal
var cropped_image := Image.new()
cropped_image = image.get_rect(
Rect2(frame_width * xx, frame_height * yy, frame_width, frame_height)
)
cropped_image.crop(project.size.x, project.size.y)
var frame_index = start_frame + image_no
cropped_image.convert(Image.FORMAT_RGBA8)
new_frames[frame_index].cels[layer_index] = (Cel.new(cropped_image, 1))
image_no += 1
cels.append(PixelCel.new(cropped_image))
else:
cels.append(layer.new_empty_cel())
project.undo_redo.add_do_property(project, "current_frame", new_frames.size() - 1)
project.undo_redo.add_do_property(project, "current_frame", new_frames_size - 1)
project.undo_redo.add_do_property(project, "current_layer", project.layers.size())
project.undo_redo.add_do_property(project, "frames", new_frames)
project.undo_redo.add_do_method(project, "add_frames", frames, frame_indices)
project.undo_redo.add_do_property(project, "layers", new_layers)
project.undo_redo.add_do_method(project, "add_layers", [layer], [project.layers.size()], [cels])
project.undo_redo.add_do_method(Global, "undo_or_redo", false)
project.undo_redo.add_undo_property(project, "current_layer", project.current_layer)
project.undo_redo.add_undo_property(project, "current_frame", project.current_frame)
project.undo_redo.add_undo_method(project, "remove_layers", [project.layers.size()])
project.undo_redo.add_undo_property(project, "layers", project.layers)
project.undo_redo.add_undo_property(project, "frames", project.frames)
project.undo_redo.add_do_method(Global, "undo_or_redo", false)
project.undo_redo.add_undo_method(project, "remove_frames", frame_indices)
project.undo_redo.add_undo_method(Global, "undo_or_redo", true)
project.undo_redo.commit_action()
func open_image_at_frame(image: Image, layer_index := 0, frame_index := 0) -> void:
func open_image_at_cel(image: Image, layer_index := 0, frame_index := 0) -> void:
var project = Global.current_project
image.crop(project.size.x, project.size.y)
project.undos += 1
project.undo_redo.create_action("Replaced Frame")
var frames: Array = []
# create a duplicate of "project.frames"
for i in project.frames.size():
var frame := Frame.new()
frame.cels = project.frames[i].cels.duplicate(true)
frames.append(frame)
project.undo_redo.create_action("Replaced Cel")
for i in project.frames.size():
if i == frame_index:
image.crop(project.size.x, project.size.y)
image.convert(Image.FORMAT_RGBA8)
frames[i].cels[layer_index] = (Cel.new(image, 1))
project.undo_redo.add_do_property(project.frames[i], "cels", frames[i].cels)
project.undo_redo.add_undo_property(project.frames[i], "cels", project.frames[i].cels)
var cel: PixelCel = project.frames[i].cels[layer_index]
project.undo_redo.add_do_property(cel, "image", image)
project.undo_redo.add_undo_property(cel, "image", cel.image)
project.undo_redo.add_do_property(project, "frames", frames)
project.undo_redo.add_do_property(project, "selected_cels", [])
project.undo_redo.add_do_property(project, "current_layer", layer_index)
project.undo_redo.add_do_property(project, "current_frame", frame_index)
project.undo_redo.add_undo_property(project, "frames", project.frames)
project.undo_redo.add_undo_property(project, "current_frame", project.current_frame)
project.undo_redo.add_do_method(Global, "undo_or_redo", false)
project.undo_redo.add_undo_property(project, "selected_cels", [])
project.undo_redo.add_undo_property(project, "current_layer", project.current_layer)
project.undo_redo.add_undo_property(project, "current_frame", project.current_frame)
project.undo_redo.add_undo_method(Global, "undo_or_redo", true)
project.undo_redo.commit_action()
@ -557,64 +536,50 @@ func open_image_at_frame(image: Image, layer_index := 0, frame_index := 0) -> vo
func open_image_as_new_frame(image: Image, layer_index := 0) -> void:
var project = Global.current_project
image.crop(project.size.x, project.size.y)
var new_frames: Array = project.frames.duplicate()
var frame := Frame.new()
for i in project.layers.size():
if i == layer_index:
image.convert(Image.FORMAT_RGBA8)
frame.cels.append(Cel.new(image, 1))
frame.cels.append(PixelCel.new(image, 1))
else:
var empty_image := Image.new()
empty_image.create(project.size.x, project.size.y, false, Image.FORMAT_RGBA8)
frame.cels.append(Cel.new(empty_image, 1))
new_frames.append(frame)
frame.cels.append(project.layers[i].new_empty_cel())
project.undos += 1
project.undo_redo.create_action("Add Frame")
project.undo_redo.add_do_method(Global, "undo_or_redo", false)
project.undo_redo.add_undo_method(Global, "undo_or_redo", true)
project.undo_redo.add_do_property(project, "frames", new_frames)
project.undo_redo.add_do_property(project, "current_frame", new_frames.size() - 1)
project.undo_redo.add_do_method(project, "add_frames", [frame], [project.frames.size()])
project.undo_redo.add_do_property(project, "current_layer", layer_index)
project.undo_redo.add_do_property(project, "current_frame", project.frames.size())
project.undo_redo.add_undo_property(project, "frames", project.frames)
project.undo_redo.add_undo_property(project, "current_frame", project.current_frame)
project.undo_redo.add_undo_method(Global, "undo_or_redo", true)
project.undo_redo.add_undo_method(project, "remove_frames", [project.frames.size()])
project.undo_redo.add_undo_property(project, "current_layer", project.current_layer)
project.undo_redo.add_undo_property(project, "current_frame", project.current_frame)
project.undo_redo.commit_action()
func open_image_as_new_layer(image: Image, file_name: String, frame_index := 0) -> void:
var project = Global.current_project
image.crop(project.size.x, project.size.y)
var new_layers: Array = Global.current_project.layers.duplicate()
var layer := Layer.new(file_name)
var layer := PixelLayer.new(project, file_name)
var cels := []
Global.current_project.undos += 1
Global.current_project.undo_redo.create_action("Add Layer")
for i in project.frames.size():
var new_cels: Array = project.frames[i].cels.duplicate(true)
if i == frame_index:
image.convert(Image.FORMAT_RGBA8)
new_cels.append(Cel.new(image, 1))
cels.append(PixelCel.new(image, 1))
else:
var empty_image := Image.new()
empty_image.create(project.size.x, project.size.y, false, Image.FORMAT_RGBA8)
new_cels.append(Cel.new(empty_image, 1))
cels.append(layer.new_empty_cel())
project.undo_redo.add_do_property(project.frames[i], "cels", new_cels)
project.undo_redo.add_undo_property(project.frames[i], "cels", project.frames[i].cels)
new_layers.append(layer)
project.undo_redo.add_do_property(project, "current_layer", new_layers.size() - 1)
project.undo_redo.add_do_property(project, "layers", new_layers)
project.undo_redo.add_do_property(project, "current_layer", project.layers.size())
project.undo_redo.add_do_method(project, "add_layers", [layer], [project.layers.size()], [cels])
project.undo_redo.add_do_property(project, "current_frame", frame_index)
project.undo_redo.add_undo_property(project, "current_layer", project.current_layer)
project.undo_redo.add_undo_property(project, "layers", project.layers)
project.undo_redo.add_undo_method(project, "remove_layers", [project.layers.size()])
project.undo_redo.add_undo_property(project, "current_frame", project.current_frame)
project.undo_redo.add_undo_method(Global, "undo_or_redo", true)

View file

@ -174,7 +174,7 @@ func _fill_new_palette_with_colors(
match get_colors_from:
GetColorsFrom.CURRENT_CEL:
for cel_index in current_project.selected_cels:
var cel: Cel = current_project.frames[cel_index[0]].cels[cel_index[1]]
var cel: PixelCel = current_project.frames[cel_index[0]].cels[cel_index[1]]
cels.append(cel)
GetColorsFrom.CURRENT_FRAME:
for cel in current_project.frames[current_project.current_frame].cels:

53
src/Classes/BaseCel.gd Normal file
View file

@ -0,0 +1,53 @@
class_name BaseCel
extends Reference
# Base class for cel properties.
# The term "cel" comes from "celluloid" (https://en.wikipedia.org/wiki/Cel).
var opacity: float
var image_texture: ImageTexture
# Methods to Override:
# The content methods deal with the unique content of each cel type. For example, an Image for
# PixelLayers, or a Dictionary of settings for a procedural layer type, and null for Groups.
# Can be used fo linking/unlinking cels, copying, and deleting content
func get_content():
return null
func set_content(_content) -> void:
return
# Can be used to delete the content of the cel with set_content
# (using the old content from get_content as undo data)
func create_empty_content():
return []
# Can be used for creating copy content for copying cels or unlinking cels
func copy_content():
return []
# Returns the image var for image based cel types, or a render for procedural types.
# It's meant for read-only usage of image data, such as copying selections or color picking.
func get_image() -> Image:
return null
func update_texture() -> void:
return
func save_image_data_to_pxo(_file: File) -> void:
return
func load_image_data_from_pxo(_file: File, _project_size: Vector2) -> void:
return
func instantiate_cel_button() -> Node:
return null

139
src/Classes/BaseLayer.gd Normal file
View file

@ -0,0 +1,139 @@
class_name BaseLayer
extends Reference
# Base class for layer properties. Different layer types extend from this class.
var name := ""
var visible := true
var locked := false
var parent: BaseLayer
var project
var index: int
# Returns true if this is a direct or indirect parent of layer
func is_a_parent_of(layer: BaseLayer) -> bool:
if layer.parent == self:
return true
elif is_instance_valid(layer.parent):
return is_a_parent_of(layer.parent)
return false
func get_children(recursive: bool) -> Array:
var children := []
if recursive:
for i in index:
if is_a_parent_of(project.layers[i]):
children.append(project.layers[i])
else:
for i in index:
if project.layers[i].parent == self:
children.append(project.layers[i])
return children
func get_child_count(recursive: bool) -> int:
var count := 0
if recursive:
for i in index:
if is_a_parent_of(project.layers[i]):
count += 1
else:
for i in index:
if project.layers[i].parent == self:
count += 1
return count
func has_children() -> bool:
if index == 0:
return false
return project.layers[index - 1].parent == self
func is_expanded_in_hierarchy() -> bool:
if is_instance_valid(parent):
return parent.expanded and parent.is_expanded_in_hierarchy()
return true
func is_visible_in_hierarchy() -> bool:
if is_instance_valid(parent) and visible:
return parent.is_visible_in_hierarchy()
return visible
func is_locked_in_hierarchy() -> bool:
if is_instance_valid(parent) and not locked:
return parent.is_locked_in_hierarchy()
return locked
func get_hierarchy_depth() -> int:
if is_instance_valid(parent):
return parent.get_hierarchy_depth() + 1
return 0
func get_layer_path() -> String:
if is_instance_valid(parent):
return str(parent.get_layer_path(), "/", name)
return name
# Methods to Override:
func serialize() -> Dictionary:
assert(index == project.layers.find(self))
return {
"name": name,
"visible": visible,
"locked": locked,
"parent": parent.index if is_instance_valid(parent) else -1
}
func deserialize(dict: Dictionary) -> void:
name = dict.name
visible = dict.visible
locked = dict.locked
if dict.get("parent", -1) != -1:
parent = project.layers[dict.parent]
func copy() -> BaseLayer:
var copy = get_script().new(project)
copy.project = project
copy.index = index
copy.deserialize(serialize())
return copy
func new_empty_cel() -> BaseCel:
return null
func copy_cel(_frame: int, _linked: bool) -> BaseCel:
return null
# Used to copy all cels with cel linking properly set up between this set of copies:
func copy_all_cels() -> Array:
return []
func set_name_to_default(number: int) -> void:
name = tr("Layer") + " %s" % number
func can_layer_get_drawn() -> bool:
return false
func accepts_child(_layer: BaseLayer) -> bool:
return false
func instantiate_layer_button() -> Node:
return null

View file

@ -1,24 +0,0 @@
class_name Cel
extends Reference
# A class for cel properties.
# The term "cel" comes from "celluloid" (https://en.wikipedia.org/wiki/Cel).
# The "image" variable is where the image data of each cel are.
var image: Image setget image_changed
var image_texture: ImageTexture
var opacity: float
func _init(_image := Image.new(), _opacity := 1.0, _image_texture: ImageTexture = null) -> void:
if _image_texture:
image_texture = _image_texture
else:
image_texture = ImageTexture.new()
self.image = _image
opacity = _opacity
func image_changed(value: Image) -> void:
image = value
if !image.is_empty():
image_texture.create_from_image(image, 0)

21
src/Classes/GroupCel.gd Normal file
View file

@ -0,0 +1,21 @@
class_name GroupCel
extends BaseCel
# A class for the properties of cels in GroupLayers.
# The term "cel" comes from "celluloid" (https://en.wikipedia.org/wiki/Cel).
func _init(_opacity := 1.0) -> void:
opacity = _opacity
image_texture = ImageTexture.new()
func get_image() -> Image:
var image = Image.new()
image.create(
Global.current_project.size.x, Global.current_project.size.y, false, Image.FORMAT_RGBA8
)
return image
func instantiate_cel_button() -> Node:
return Global.group_cel_button_node.instance()

54
src/Classes/GroupLayer.gd Normal file
View file

@ -0,0 +1,54 @@
class_name GroupLayer
extends BaseLayer
# A class for group layer properties
var expanded := true
func _init(_project, _name := "") -> void:
project = _project
name = _name
# Overridden Methods:
func serialize() -> Dictionary:
var data = .serialize()
data["type"] = Global.LayerTypes.GROUP
data["expanded"] = expanded
return data
func deserialize(dict: Dictionary) -> void:
.deserialize(dict)
expanded = dict.expanded
func new_empty_cel() -> BaseCel:
return GroupCel.new()
func copy_cel(frame_index: int, _linked: bool) -> BaseCel:
var cel: GroupCel = project.frames[frame_index].cels[index]
return GroupCel.new(cel.opacity)
func copy_all_cels() -> Array:
var cels := []
for frame in project.frames:
var cel: GroupCel = frame.cels[index]
cels.append(GroupCel.new(cel.opacity))
return cels
func set_name_to_default(number: int) -> void:
name = tr("Group") + " %s" % number
func accepts_child(_layer: BaseLayer) -> bool:
return true
func instantiate_layer_button() -> Node:
return Global.group_layer_button_node.instance()

View file

@ -55,7 +55,7 @@ func _confirmed() -> void:
for cel_index in project.selected_cels:
if !project.layers[cel_index[1]].can_layer_get_drawn():
continue
var cel: Cel = project.frames[cel_index[0]].cels[cel_index[1]]
var cel: PixelCel = project.frames[cel_index[0]].cels[cel_index[1]]
var cel_image: Image = cel.image
commit_action(cel_image)
_commit_undo("Draw", undo_data, project)
@ -126,12 +126,14 @@ func _get_selected_draw_images(project: Project) -> Array: # Array of Images
var images := []
if affect == SELECTED_CELS:
for cel_index in project.selected_cels:
var cel: Cel = project.frames[cel_index[0]].cels[cel_index[1]]
images.append(cel.image)
var cel: BaseCel = project.frames[cel_index[0]].cels[cel_index[1]]
if cel is PixelCel:
images.append(cel.image)
else:
for frame in project.frames:
for cel in frame.cels:
images.append(cel.image)
if cel is PixelCel:
images.append(cel.image)
return images

View file

@ -1,23 +0,0 @@
class_name Layer
extends Reference
# A class for layer properties.
var name := ""
var visible := true
var locked := false
var new_cels_linked := false
var linked_cels := [] # Array of Frames
func _init(
_name := "", _visible := true, _locked := false, _new_cels_linked := false, _linked_cels := []
) -> void:
name = _name
visible = _visible
locked = _locked
new_cels_linked = _new_cels_linked
linked_cels = _linked_cels
func can_layer_get_drawn() -> bool:
return visible && !locked

69
src/Classes/PixelCel.gd Normal file
View file

@ -0,0 +1,69 @@
class_name PixelCel
extends BaseCel
# A class for the properties of cels in PixelLayers.
# The term "cel" comes from "celluloid" (https://en.wikipedia.org/wiki/Cel).
# The "image" variable is where the image data of each cel are.
var image: Image setget image_changed
func _init(_image := Image.new(), _opacity := 1.0, _image_texture: ImageTexture = null) -> void:
if _image_texture:
image_texture = _image_texture
else:
image_texture = ImageTexture.new()
self.image = _image # Set image and call setter
opacity = _opacity
func image_changed(value: Image) -> void:
image = value
if !image.is_empty():
image_texture.create_from_image(image, 0)
func get_content():
return image
func set_content(content) -> void:
image = content
image_texture.create_from_image(image, 0)
func create_empty_content():
var empty_image := Image.new()
empty_image.create(image.get_size().x, image.get_size().y, false, Image.FORMAT_RGBA8)
return empty_image
func copy_content():
var copy_image := Image.new()
copy_image.create_from_data(
image.get_width(), image.get_height(), false, Image.FORMAT_RGBA8, image.get_data()
)
return copy_image
func get_image() -> Image:
return image
func update_texture() -> void:
image_texture.set_data(image)
func save_image_data_to_pxo(file: File) -> void:
file.store_buffer(image.get_data())
func load_image_data_from_pxo(file: File, project_size: Vector2) -> void:
var buffer := file.get_buffer(project_size.x * project_size.y * 4)
image.create_from_data(project_size.x, project_size.y, false, Image.FORMAT_RGBA8, buffer)
image_changed(image)
func instantiate_cel_button() -> Node:
var cel_button = Global.pixel_cel_button_node.instance()
cel_button.get_child(0).texture = image_texture
return cel_button

82
src/Classes/PixelLayer.gd Normal file
View file

@ -0,0 +1,82 @@
class_name PixelLayer
extends BaseLayer
# A class for standard pixel layer properties.
var new_cels_linked := false
var linked_cels := [] # Array of Frames
func _init(_project, _name := "") -> void:
project = _project
name = _name
# Overridden Methods:
func serialize() -> Dictionary:
var dict = .serialize()
dict["type"] = Global.LayerTypes.PIXEL
dict["new_cels_linked"] = new_cels_linked
dict["linked_cels"] = []
for cel in linked_cels:
dict.linked_cels.append(project.frames.find(cel))
return dict
func deserialize(dict: Dictionary) -> void:
.deserialize(dict)
new_cels_linked = dict.new_cels_linked
for linked_cel_number in dict.linked_cels:
linked_cels.append(project.frames[linked_cel_number])
var linked_cel: PixelCel = project.frames[linked_cel_number].cels[index]
linked_cel.image = linked_cels[0].cels[index].image
linked_cel.image_texture = linked_cels[0].cels[index].image_texture
func new_empty_cel() -> BaseCel:
var image := Image.new()
image.create(project.size.x, project.size.y, false, Image.FORMAT_RGBA8)
return PixelCel.new(image)
func copy_cel(frame_index: int, linked: bool) -> BaseCel:
if linked and not linked_cels.empty():
var cel: PixelCel = linked_cels[0].cels[index]
return PixelCel.new(cel.image, cel.opacity, cel.image_texture)
else:
var cel: PixelCel = project.frames[frame_index].cels[index]
var copy_image := Image.new()
copy_image.copy_from(cel.image)
return PixelCel.new(copy_image, cel.opacity)
func copy_all_cels() -> Array:
var cels := []
var linked_image: Image
var linked_texture: ImageTexture
if not linked_cels.empty():
var cel: PixelCel = linked_cels[0].cels[index]
linked_image = Image.new()
linked_image.copy_from(cel.image)
linked_texture = ImageTexture.new()
for frame in project.frames:
var cel: PixelCel = frame.cels[index]
if linked_cels.has(frame):
cels.append(PixelCel.new(linked_image, cel.opacity, linked_texture))
else:
var copy_image := Image.new()
copy_image.copy_from(cel.image)
cels.append(PixelCel.new(copy_image, cel.opacity))
return cels
func can_layer_get_drawn() -> bool:
return is_visible_in_hierarchy() && !is_locked_in_hierarchy()
func instantiate_layer_button() -> Node:
return Global.pixel_layer_button_node.instance()

View file

@ -1,3 +1,4 @@
# gdlint: ignore=max-public-methods
class_name Project
extends Reference
# A class for project properties.
@ -9,8 +10,11 @@ var tiles: Tiles
var undos := 0 # The number of times we added undo properties
var fill_color := Color(0)
var has_changed := false setget _has_changed_changed
var frames := [] setget _frames_changed # Array of Frames (that contain Cels)
var layers := [] setget _layers_changed # Array of Layers
# frames and layers Arrays should generally only be modified directly when
# opening/creating a project. When modifiying the current project, use
# the add/remove/move/swap_frames/layers methods
var frames := [] # Array of Frames (that contain Cels)
var layers := [] # Array of Layers
var current_frame := 0 setget _frame_changed
var current_layer := 0 setget _layer_changed
var selected_cels := [[0, 0]] # Array of Arrays of 2 integers (frame & layer)
@ -44,9 +48,6 @@ var file_format: int = Export.FileFormat.PNG
var was_exported := false
var export_overwrite := false
var frame_button_node = preload("res://src/UI/Timeline/FrameButton.tscn")
var layer_button_node = preload("res://src/UI/Timeline/LayerButton.tscn")
var cel_button_node = preload("res://src/UI/Timeline/CelButton.tscn")
var animation_tag_node = preload("res://src/UI/Timeline/AnimationTagUI.tscn")
@ -88,6 +89,8 @@ func remove() -> void:
undo_redo.free()
for guide in guides:
guide.queue_free()
# Prevents memory leak (due to the layers' project reference stopping ref counting from freeing)
layers.clear()
Global.projects.erase(self)
@ -108,13 +111,11 @@ func new_empty_frame() -> Frame:
var frame := Frame.new()
var bottom_layer := true
for l in layers: # Create as many cels as there are layers
var image := Image.new()
image.create(size.x, size.y, false, Image.FORMAT_RGBA8)
if bottom_layer and fill_color.a > 0:
image.fill(fill_color)
frame.cels.append(Cel.new(image, 1))
var cel: BaseCel = l.new_empty_cel()
if cel is PixelCel and bottom_layer and fill_color.a > 0:
cel.image.fill(fill_color)
frame.cels.append(cel)
bottom_layer = false
return frame
@ -135,51 +136,7 @@ func _selection_offset_changed(value: Vector2) -> void:
func change_project() -> void:
# Remove old nodes
for container in Global.layers_container.get_children():
container.queue_free()
_remove_cel_buttons()
for frame_id in Global.frame_ids.get_children():
Global.frame_ids.remove_child(frame_id)
frame_id.queue_free()
# Create new ones
for i in range(layers.size() - 1, -1, -1):
# Create layer buttons
var layer_container = layer_button_node.instance()
layer_container.layer = i
if layers[i].name == "":
layers[i].name = tr("Layer") + " %s" % i
Global.layers_container.add_child(layer_container)
layer_container.label.text = layers[i].name
layer_container.line_edit.text = layers[i].name
var layer_cel_container := HBoxContainer.new()
Global.frames_container.add_child(layer_cel_container)
for j in range(frames.size()): # Create Cel buttons
var cel_button = cel_button_node.instance()
cel_button.frame = j
cel_button.layer = i
cel_button.get_child(0).texture = frames[j].cels[i].image_texture
cel_button.pressed = j == current_frame and i == current_layer
layer_cel_container.add_child(cel_button)
for j in range(frames.size()): # Create frame buttons
var button: Button = frame_button_node.instance()
button.frame = j
button.rect_min_size.x = Global.animation_timeline.cel_size
button.text = str(j + 1)
button.pressed = j == current_frame
Global.frame_ids.add_child(button)
var layer_button = Global.layers_container.get_child(
Global.layers_container.get_child_count() - 1 - current_layer
)
layer_button.pressed = true
Global.animation_timeline.project_changed()
Global.current_frame_mark_label.text = "%s/%s" % [str(current_frame + 1), frames.size()]
@ -188,8 +145,7 @@ func change_project() -> void:
Global.disable_button(
Global.move_right_frame_button, frames.size() == 1 or current_frame == frames.size() - 1
)
_toggle_layer_buttons_layers()
_toggle_layer_buttons_current_layer()
toggle_layer_buttons()
self.animation_tags = animation_tags
@ -292,20 +248,8 @@ func change_project() -> void:
func serialize() -> Dictionary:
var layer_data := []
for layer in layers:
var linked_cels := []
for cel in layer.linked_cels:
linked_cels.append(frames.find(cel))
layer_data.append(
{
"name": layer.name,
"visible": layer.visible,
"locked": layer.locked,
"new_cels_linked": layer.new_cels_linked,
"linked_cels": linked_cels,
"metadata": _serialize_metadata(layer)
}
)
layer_data.append(layer.serialize())
layer_data[-1]["metadata"] = _serialize_metadata(layer)
var tag_data := []
for tag in animation_tags:
@ -395,14 +339,19 @@ func deserialize(dict: Dictionary) -> void:
tiles.y_basis.y = dict.tile_mode_y_basis_y
if dict.has("save_path"):
OpenSave.current_save_paths[Global.projects.find(self)] = dict.save_path
if dict.has("frames"):
if dict.has("frames") and dict.has("layers"):
var frame_i := 0
for frame in dict.frames:
var cels := []
var cel_i := 0
for cel in frame.cels:
var cel_class := Cel.new(Image.new(), cel.opacity)
_deserialize_metadata(cel_class, cel)
cels.append(cel_class)
match int(dict.layers[cel_i].get("type", Global.LayerTypes.PIXEL)):
Global.LayerTypes.PIXEL:
cels.append(PixelCel.new(Image.new(), cel.opacity))
Global.LayerTypes.GROUP:
cels.append(GroupCel.new(cel.opacity))
_deserialize_metadata(cels[cel_i], cel)
cel_i += 1
var duration := 1.0
if frame.has("duration"):
duration = frame.duration
@ -414,25 +363,18 @@ func deserialize(dict: Dictionary) -> void:
frames.append(frame_class)
frame_i += 1
if dict.has("layers"):
var layer_i := 0
for saved_layer in dict.layers:
var linked_cels := []
for linked_cel_number in saved_layer.linked_cels:
linked_cels.append(frames[linked_cel_number])
var linked_cel: Cel = frames[linked_cel_number].cels[layer_i]
linked_cel.image = linked_cels[0].cels[layer_i].image
linked_cel.image_texture = linked_cels[0].cels[layer_i].image_texture
var layer := Layer.new(
saved_layer.name,
saved_layer.visible,
saved_layer.locked,
saved_layer.new_cels_linked,
linked_cels
)
_deserialize_metadata(layer, saved_layer)
layers.append(layer)
layer_i += 1
for saved_layer in dict.layers:
match int(saved_layer.get("type", Global.LayerTypes.PIXEL)):
Global.LayerTypes.PIXEL:
layers.append(PixelLayer.new(self))
Global.LayerTypes.GROUP:
layers.append(GroupLayer.new(self))
# Parent references to other layers are created when deserializing
# a layer, so loop again after creating them:
for layer_i in dict.layers.size():
layers[layer_i].index = layer_i
layers[layer_i].deserialize(dict.layers[layer_i])
_deserialize_metadata(layers[layer_i], dict.layers[layer_i])
if dict.has("tags"):
for tag in dict.tags:
animation_tags.append(AnimationTag.new(tag.name, Color(tag.color), tag.from, tag.to))
@ -502,84 +444,6 @@ func _size_changed(value: Vector2) -> void:
size = value
func _frames_changed(value: Array) -> void:
Global.canvas.selection.transform_content_confirm()
frames = value
selected_cels.clear()
_remove_cel_buttons()
for frame_id in Global.frame_ids.get_children():
Global.frame_ids.remove_child(frame_id)
frame_id.queue_free()
for i in range(layers.size() - 1, -1, -1):
var layer_cel_container := HBoxContainer.new()
layer_cel_container.name = "FRAMESS " + str(i)
Global.frames_container.add_child(layer_cel_container)
for j in range(frames.size()):
var cel_button = cel_button_node.instance()
cel_button.frame = j
cel_button.layer = i
cel_button.get_child(0).texture = frames[j].cels[i].image_texture
layer_cel_container.add_child(cel_button)
for j in range(frames.size()):
var button: Button = frame_button_node.instance()
button.frame = j
button.rect_min_size.x = Global.animation_timeline.cel_size
button.text = str(j + 1)
Global.frame_ids.add_child(button)
_set_timeline_first_and_last_frames()
func _layers_changed(value: Array) -> void:
layers = value
if Global.layers_changed_skip:
Global.layers_changed_skip = false
return
selected_cels.clear()
for container in Global.layers_container.get_children():
container.queue_free()
_remove_cel_buttons()
for i in range(layers.size() - 1, -1, -1):
var layer_button: LayerButton = layer_button_node.instance()
layer_button.layer = i
if layers[i].name == "":
layers[i].name = tr("Layer") + " %s" % i
Global.layers_container.add_child(layer_button)
layer_button.label.text = layers[i].name
layer_button.line_edit.text = layers[i].name
var layer_cel_container := HBoxContainer.new()
layer_cel_container.name = "LAYERSSS " + str(i)
Global.frames_container.add_child(layer_cel_container)
for j in range(frames.size()):
var cel_button = cel_button_node.instance()
cel_button.frame = j
cel_button.layer = i
cel_button.get_child(0).texture = frames[j].cels[i].image_texture
layer_cel_container.add_child(cel_button)
var layer_button = Global.layers_container.get_child(
Global.layers_container.get_child_count() - 1 - current_layer
)
layer_button.pressed = true
self.current_frame = current_frame # Call frame_changed to update UI
_toggle_layer_buttons_layers()
func _remove_cel_buttons() -> void:
for container in Global.frames_container.get_children():
Global.frames_container.remove_child(container)
container.queue_free()
func _frame_changed(value: int) -> void:
Global.canvas.selection.transform_content_confirm()
current_frame = value
@ -596,32 +460,25 @@ func _frame_changed(value: int) -> void:
selected_cels.append([current_frame, current_layer])
# Select the new frame
for cel in selected_cels:
var current_frame_tmp: int = cel[0]
var current_layer_tmp: int = cel[1]
if current_frame_tmp < Global.frame_ids.get_child_count():
var frame_button: BaseButton = Global.frame_ids.get_child(current_frame_tmp)
var frame: int = cel[0]
var layer: int = cel[1]
if frame < Global.frame_ids.get_child_count():
var frame_button: BaseButton = Global.frame_ids.get_child(frame)
frame_button.pressed = true
var container_child_count: int = Global.frames_container.get_child_count()
if current_layer_tmp < container_child_count:
var container = Global.frames_container.get_child(
container_child_count - 1 - current_layer_tmp
)
if current_frame_tmp < container.get_child_count():
var fbutton = container.get_child(current_frame_tmp)
fbutton.pressed = true
Global.disable_button(Global.remove_frame_button, frames.size() == 1)
Global.disable_button(Global.move_left_frame_button, frames.size() == 1 or current_frame == 0)
Global.disable_button(
Global.move_right_frame_button, frames.size() == 1 or current_frame == frames.size() - 1
)
if layer < container_child_count:
var container = Global.frames_container.get_child(container_child_count - 1 - layer)
if frame < container.get_child_count():
var cel_button = container.get_child(frame)
cel_button.pressed = true
if current_frame < frames.size():
var cel_opacity: float = frames[current_frame].cels[current_layer].opacity
Global.layer_opacity_slider.value = cel_opacity * 100
Global.layer_opacity_spinbox.value = cel_opacity * 100
toggle_frame_buttons()
Global.canvas.update()
Global.transparent_checker.update_rect()
@ -630,7 +487,7 @@ func _layer_changed(value: int) -> void:
Global.canvas.selection.transform_content_confirm()
current_layer = value
_toggle_layer_buttons_current_layer()
toggle_layer_buttons()
yield(Global.get_tree().create_timer(0.01), "timeout")
self.current_frame = current_frame # Call frame_changed to update UI
@ -638,48 +495,44 @@ func _layer_changed(value: int) -> void:
layer_button.pressed = false
for cel in selected_cels:
var current_layer_tmp: int = cel[1]
if current_layer_tmp < Global.layers_container.get_child_count():
var layer: int = cel[1]
if layer < Global.layers_container.get_child_count():
var layer_button = Global.layers_container.get_child(
Global.layers_container.get_child_count() - 1 - current_layer_tmp
Global.layers_container.get_child_count() - 1 - layer
)
layer_button.pressed = true
func _toggle_layer_buttons_layers() -> void:
if !layers:
func toggle_frame_buttons() -> void:
Global.disable_button(Global.remove_frame_button, frames.size() == 1)
Global.disable_button(Global.move_left_frame_button, frames.size() == 1 or current_frame == 0)
Global.disable_button(
Global.move_right_frame_button, frames.size() == 1 or current_frame == frames.size() - 1
)
func toggle_layer_buttons() -> void:
if layers.empty() or current_layer >= layers.size():
return
if layers[current_layer].locked:
Global.disable_button(Global.remove_layer_button, true)
var child_count: int = layers[current_layer].get_child_count(true)
if layers.size() == 1:
Global.disable_button(Global.remove_layer_button, true)
Global.disable_button(Global.move_up_layer_button, true)
Global.disable_button(Global.move_down_layer_button, true)
Global.disable_button(Global.merge_down_layer_button, true)
elif !layers[current_layer].locked:
Global.disable_button(Global.remove_layer_button, false)
func _toggle_layer_buttons_current_layer() -> void:
if current_layer < layers.size() - 1:
Global.disable_button(Global.move_up_layer_button, false)
else:
Global.disable_button(Global.move_up_layer_button, true)
if current_layer > 0:
Global.disable_button(Global.move_down_layer_button, false)
Global.disable_button(Global.merge_down_layer_button, false)
else:
Global.disable_button(Global.move_down_layer_button, true)
Global.disable_button(Global.merge_down_layer_button, true)
if current_layer < layers.size():
if layers[current_layer].locked:
Global.disable_button(Global.remove_layer_button, true)
else:
if layers.size() > 1:
Global.disable_button(Global.remove_layer_button, false)
Global.disable_button(
Global.remove_layer_button,
layers[current_layer].is_locked_in_hierarchy() or layers.size() == child_count + 1
)
Global.disable_button(Global.move_up_layer_button, current_layer == layers.size() - 1)
Global.disable_button(
Global.move_down_layer_button,
current_layer == child_count and not is_instance_valid(layers[current_layer].parent)
)
Global.disable_button(
Global.merge_down_layer_button,
(
current_layer == child_count
or layers[current_layer] is GroupLayer
or layers[current_layer - 1] is GroupLayer
)
)
func _animation_tags_changed(value: Array) -> void:
@ -735,6 +588,7 @@ func is_empty() -> bool:
return (
frames.size() == 1
and layers.size() == 1
and layers[0] is PixelLayer
and frames[0].cels[0].image.is_invisible()
and animation_tags.size() == 0
)
@ -745,15 +599,10 @@ func duplicate_layers() -> Array:
# Loop through the array to create new classes for each element, so that they
# won't be the same as the original array's classes. Needed for undo/redo to work properly.
for i in new_layers.size():
var new_linked_cels = new_layers[i].linked_cels.duplicate()
new_layers[i] = Layer.new(
new_layers[i].name,
new_layers[i].visible,
new_layers[i].locked,
new_layers[i].new_cels_linked,
new_linked_cels
)
new_layers[i] = new_layers[i].copy()
for l in new_layers:
if is_instance_valid(l.parent):
l.parent = new_layers[l.parent.index] # Update the parent to the new copy of the parent
return new_layers
@ -776,3 +625,236 @@ func can_pixel_get_drawn(
return image.is_pixel_selected(pixel)
else:
return true
# Timeline modifications
# Modifying layers or frames Arrays on the current project should generally only be done
# through these methods.
# These allow you to add/remove/move/swap frames/layers/cels. It updates the Animation Timeline
# UI, and updates indices. These are designed to be reversible, meaning that to undo an add, you
# use remove, and vise versa. To undo a move or swap, use move or swap with the paramaters swapped.
func add_frames(new_frames: Array, indices: Array) -> void: # indices should be in ascending order
Global.canvas.selection.transform_content_confirm()
selected_cels.clear()
for i in new_frames.size():
frames.insert(indices[i], new_frames[i])
Global.animation_timeline.project_frame_added(indices[i])
# Update the frames and frame buttons:
for f in frames.size():
Global.frame_ids.get_child(f).frame = f
Global.frame_ids.get_child(f).text = str(f + 1)
# Update the cel buttons:
for l in layers.size():
var layer_cel_container = Global.frames_container.get_child(layers.size() - 1 - l)
for f in frames.size():
layer_cel_container.get_child(f).frame = f
layer_cel_container.get_child(f).button_setup()
_set_timeline_first_and_last_frames()
func remove_frames(indices: Array) -> void: # indices should be in ascending order
Global.canvas.selection.transform_content_confirm()
selected_cels.clear()
for i in indices.size():
# With each removed index, future indices need to be lowered, so subtract by i
frames.remove(indices[i] - i)
Global.animation_timeline.project_frame_removed(indices[i] - i)
# Update the frames and frame buttons:
for f in frames.size():
Global.frame_ids.get_child(f).frame = f
Global.frame_ids.get_child(f).text = str(f + 1)
# Update the cel buttons:
for l in layers.size():
var layer_cel_container = Global.frames_container.get_child(layers.size() - 1 - l)
for f in frames.size():
layer_cel_container.get_child(f).frame = f
layer_cel_container.get_child(f).button_setup()
_set_timeline_first_and_last_frames()
func move_frame(from_index: int, to_index: int) -> void:
Global.canvas.selection.transform_content_confirm()
selected_cels.clear()
var frame = frames[from_index]
frames.remove(from_index)
Global.animation_timeline.project_frame_removed(from_index)
frames.insert(to_index, frame)
Global.animation_timeline.project_frame_added(to_index)
# Update the frames and frame buttons:
for f in frames.size():
Global.frame_ids.get_child(f).frame = f
Global.frame_ids.get_child(f).text = str(f + 1)
# Update the cel buttons:
for l in layers.size():
var layer_cel_container = Global.frames_container.get_child(layers.size() - 1 - l)
for f in frames.size():
layer_cel_container.get_child(f).frame = f
layer_cel_container.get_child(f).button_setup()
_set_timeline_first_and_last_frames()
func swap_frame(a_index: int, b_index: int) -> void:
Global.canvas.selection.transform_content_confirm()
selected_cels.clear()
var temp: Frame = frames[a_index]
frames[a_index] = frames[b_index]
frames[b_index] = temp
Global.animation_timeline.project_frame_removed(a_index)
Global.animation_timeline.project_frame_added(a_index)
Global.animation_timeline.project_frame_removed(b_index)
Global.animation_timeline.project_frame_added(b_index)
_set_timeline_first_and_last_frames()
func add_layers(new_layers: Array, indices: Array, cels: Array) -> void: # cels is 2d Array of cels
Global.canvas.selection.transform_content_confirm()
selected_cels.clear()
for i in indices.size():
layers.insert(indices[i], new_layers[i])
for f in frames.size():
frames[f].cels.insert(indices[i], cels[i][f])
new_layers[i].project = self
Global.animation_timeline.project_layer_added(indices[i])
# Update the layer indices and layer/cel buttons:
for l in layers.size():
layers[l].index = l
Global.layers_container.get_child(layers.size() - 1 - l).layer = l
var layer_cel_container = Global.frames_container.get_child(layers.size() - 1 - l)
for f in frames.size():
layer_cel_container.get_child(f).layer = l
layer_cel_container.get_child(f).button_setup()
toggle_layer_buttons()
func remove_layers(indices: Array) -> void:
Global.canvas.selection.transform_content_confirm()
selected_cels.clear()
for i in indices.size():
# With each removed index, future indices need to be lowered, so subtract by i
layers.remove(indices[i] - i)
for frame in frames:
frame.cels.remove(indices[i] - i)
Global.animation_timeline.project_layer_removed(indices[i] - i)
# Update the layer indices and layer/cel buttons:
for l in layers.size():
layers[l].index = l
Global.layers_container.get_child(layers.size() - 1 - l).layer = l
var layer_cel_container = Global.frames_container.get_child(layers.size() - 1 - l)
for f in frames.size():
layer_cel_container.get_child(f).layer = l
layer_cel_container.get_child(f).button_setup()
toggle_layer_buttons()
# from_indices and to_indicies should be in ascending order
func move_layers(from_indices: Array, to_indices: Array, to_parents: Array) -> void:
Global.canvas.selection.transform_content_confirm()
selected_cels.clear()
var removed_layers := []
var removed_cels := [] # 2D array of cels (an array for each layer removed)
for i in from_indices.size():
# With each removed index, future indices need to be lowered, so subtract by i
removed_layers.append(layers.pop_at(from_indices[i] - i))
removed_layers[i].parent = to_parents[i] # parents must be set before UI created in next loop
removed_cels.append([])
for frame in frames:
removed_cels[i].append(frame.cels.pop_at(from_indices[i] - i))
Global.animation_timeline.project_layer_removed(from_indices[i] - i)
for i in to_indices.size():
layers.insert(to_indices[i], removed_layers[i])
for f in frames.size():
frames[f].cels.insert(to_indices[i], removed_cels[i][f])
Global.animation_timeline.project_layer_added(to_indices[i])
# Update the layer indices and layer/cel buttons:
for l in layers.size():
layers[l].index = l
Global.layers_container.get_child(layers.size() - 1 - l).layer = l
var layer_cel_container = Global.frames_container.get_child(layers.size() - 1 - l)
for f in frames.size():
layer_cel_container.get_child(f).layer = l
layer_cel_container.get_child(f).button_setup()
toggle_layer_buttons()
# "a" and "b" should both contain "from", "to", and "to_parents" arrays.
# (Using dictionaries because there seems to be a limit of 5 arguments for do/undo method calls)
func swap_layers(a: Dictionary, b: Dictionary) -> void:
Global.canvas.selection.transform_content_confirm()
selected_cels.clear()
var a_layers := []
var b_layers := []
var a_cels := [] # 2D array of cels (an array for each layer removed)
var b_cels := [] # 2D array of cels (an array for each layer removed)
for i in a.from.size():
a_layers.append(layers.pop_at(a.from[i] - i))
Global.animation_timeline.project_layer_removed(a.from[i] - i)
a_layers[i].parent = a.to_parents[i] # All parents must be set early, before creating buttons
a_cels.append([])
for frame in frames:
a_cels[i].append(frame.cels.pop_at(a.from[i] - i))
for i in b.from.size():
var index = (b.from[i] - i) if a.from[0] > b.from[0] else (b.from[i] - i - a.from.size())
b_layers.append(layers.pop_at(index))
Global.animation_timeline.project_layer_removed(index)
b_layers[i].parent = b.to_parents[i] # All parents must be set early, before creating buttons
b_cels.append([])
for frame in frames:
b_cels[i].append(frame.cels.pop_at(index))
for i in a_layers.size():
var index = a.to[i] if a.to[0] < b.to[0] else (a.to[i] - b.to.size())
layers.insert(index, a_layers[i])
for f in frames.size():
frames[f].cels.insert(index, a_cels[i][f])
Global.animation_timeline.project_layer_added(index)
for i in b_layers.size():
layers.insert(b.to[i], b_layers[i])
for f in frames.size():
frames[f].cels.insert(b.to[i], b_cels[i][f])
Global.animation_timeline.project_layer_added(b.to[i])
# Update the layer indices and layer/cel buttons:
for l in layers.size():
layers[l].index = l
Global.layers_container.get_child(layers.size() - 1 - l).layer = l
var layer_cel_container = Global.frames_container.get_child(layers.size() - 1 - l)
for f in frames.size():
layer_cel_container.get_child(f).layer = l
layer_cel_container.get_child(f).button_setup()
toggle_layer_buttons()
func move_cel(from_frame: int, to_frame: int, layer: int) -> void:
Global.canvas.selection.transform_content_confirm()
selected_cels.clear()
var cel: BaseCel = frames[from_frame].cels[layer]
if from_frame < to_frame:
for f in range(from_frame, to_frame): # Forward range
frames[f].cels[layer] = frames[f + 1].cels[layer] # Move left
else:
for f in range(from_frame, to_frame, -1): # Backward range
frames[f].cels[layer] = frames[f - 1].cels[layer] # Move right
frames[to_frame].cels[layer] = cel
Global.animation_timeline.project_cel_removed(from_frame, layer)
Global.animation_timeline.project_cel_added(to_frame, layer)
# Update the cel buttons for this layer:
var layer_cel_container = Global.frames_container.get_child(layers.size() - 1 - layer)
for f in frames.size():
layer_cel_container.get_child(f).frame = f
layer_cel_container.get_child(f).button_setup()
func swap_cel(a_frame: int, a_layer: int, b_frame: int, b_layer: int) -> void:
Global.canvas.selection.transform_content_confirm()
selected_cels.clear()
var temp: BaseCel = frames[a_frame].cels[a_layer]
frames[a_frame].cels[a_layer] = frames[b_frame].cels[b_layer]
frames[b_frame].cels[b_layer] = temp
Global.animation_timeline.project_cel_removed(a_frame, a_layer)
Global.animation_timeline.project_cel_added(a_frame, a_layer)
Global.animation_timeline.project_cel_removed(b_frame, b_layer)
Global.animation_timeline.project_cel_added(b_frame, b_layer)

View file

@ -26,10 +26,10 @@ func _ready() -> void:
Global.window_title = tr("untitled") + " - Pixelorama " + Global.current_version
Global.current_project.layers.append(Layer.new())
var frame: Frame = Global.current_project.new_empty_frame()
Global.current_project.frames.append(frame)
Global.current_project.layers = Global.current_project.layers
Global.current_project.layers.append(PixelLayer.new(Global.current_project))
Global.current_project.frames.append(Global.current_project.new_empty_frame())
Global.animation_timeline.project_changed()
Global.current_project.toggle_frame_buttons()
Import.import_brushes(Global.directory_module.get_brushes_search_path_in_order())
Import.import_patterns(Global.directory_module.get_patterns_search_path_in_order())

View file

@ -87,14 +87,14 @@ func _get_draw_rect() -> Rect2:
func _get_draw_image() -> Image:
var project: Project = Global.current_project
return project.frames[project.current_frame].cels[project.current_layer].image
return project.frames[project.current_frame].cels[project.current_layer].get_image()
func _get_selected_draw_images() -> Array: # Array of Images
var images := []
var project: Project = Global.current_project
for cel_index in project.selected_cels:
var cel: Cel = project.frames[cel_index[0]].cels[cel_index[1]]
var cel: BaseCel = project.frames[cel_index[0]].cels[cel_index[1]]
if project.layers[cel_index[1]].can_layer_get_drawn():
images.append(cel.image)
return images

View file

@ -577,9 +577,11 @@ func _get_undo_data() -> Dictionary:
cels.append(project.frames[cel_index[0]].cels[cel_index[1]])
else:
for frame in project.frames:
var cel: Cel = frame.cels[project.current_layer]
var cel: PixelCel = frame.cels[project.current_layer]
cels.append(cel)
for cel in cels:
if cel is GroupCel:
continue
var image: Image = cel.image
image.unlock()
data[image] = image.data

View file

@ -153,7 +153,7 @@ func _get_undo_data() -> Dictionary:
cels.append(project.frames[cel_index[0]].cels[cel_index[1]])
else:
for frame in project.frames:
var cel: Cel = frame.cels[project.current_layer]
var cel: PixelCel = frame.cels[project.current_layer]
cels.append(cel)
for cel in cels:
var image: Image = cel.image

View file

@ -41,8 +41,10 @@ func _draw() -> void:
draw_set_transform(position_tmp, rotation, scale_tmp)
# Draw current frame layers
for i in range(Global.current_project.layers.size()):
if current_cels[i] is GroupCel:
continue
var modulate_color := Color(1, 1, 1, current_cels[i].opacity)
if Global.current_project.layers[i].visible: # if it's visible
if Global.current_project.layers[i].is_visible_in_hierarchy():
if i == current_layer:
draw_texture(current_cels[i].image_texture, move_preview_location, modulate_color)
else:
@ -121,16 +123,8 @@ func update_texture(layer_i: int, frame_i := -1, project: Project = Global.curre
frame_i = project.current_frame
if frame_i < project.frames.size() and layer_i < project.layers.size():
var current_cel: Cel = project.frames[frame_i].cels[layer_i]
current_cel.image_texture.set_data(current_cel.image)
if project == Global.current_project:
var container_index = Global.frames_container.get_child_count() - 1 - layer_i
var layer_cel_container = Global.frames_container.get_child(container_index)
var cel_button = layer_cel_container.get_child(frame_i)
var cel_texture_rect: TextureRect
cel_texture_rect = cel_button.find_node("CelTexture")
cel_texture_rect.texture = current_cel.image_texture
var current_cel: BaseCel = project.frames[frame_i].cels[layer_i]
current_cel.update_texture()
func update_selected_cels_textures(project: Project = Global.current_project) -> void:
@ -138,15 +132,8 @@ func update_selected_cels_textures(project: Project = Global.current_project) ->
var frame_index: int = cel_index[0]
var layer_index: int = cel_index[1]
if frame_index < project.frames.size() and layer_index < project.layers.size():
var current_cel: Cel = project.frames[frame_index].cels[layer_index]
current_cel.image_texture.set_data(current_cel.image)
if project == Global.current_project:
var container_index = Global.frames_container.get_child_count() - 1 - layer_index
var layer_cel_container = Global.frames_container.get_child(container_index)
var cel_button = layer_cel_container.get_child(frame_index)
var cel_texture_rect: TextureRect = cel_button.find_node("CelTexture")
cel_texture_rect.texture = current_cel.image_texture
var current_cel: BaseCel = project.frames[frame_index].cels[layer_index]
current_cel.update_texture()
func refresh_onion() -> void:

View file

@ -20,8 +20,13 @@ func _draw() -> void:
# Draw current frame layers
for i in range(current_cels.size()):
if current_cels[i] is GroupCel:
continue
var modulate_color := Color(1, 1, 1, current_cels[i].opacity)
if i < current_project.layers.size() and current_project.layers[i].visible:
if (
i < current_project.layers.size()
and current_project.layers[i].is_visible_in_hierarchy()
):
draw_texture(current_cels[i].image_texture, Vector2.ZERO, modulate_color)

View file

@ -4,6 +4,11 @@ extends Node2D
func _draw() -> void:
var current_cels: Array = Global.current_project.frames[Global.current_project.current_frame].cels
for i in range(Global.current_project.layers.size()):
if Global.current_project.layers[i].visible and current_cels[i].opacity > 0:
if current_cels[i] is GroupCel:
continue
if (
Global.current_project.layers[i].is_visible_in_hierarchy()
and current_cels[i].opacity > 0
):
var modulate_color := Color(1, 1, 1, current_cels[i].opacity)
draw_texture(current_cels[i].image_texture, Vector2.ZERO, modulate_color)

View file

@ -28,8 +28,8 @@ func _draw() -> void:
if change == clamp(change, 0, Global.current_project.frames.size() - 1):
var layer_i := 0
for cel in Global.current_project.frames[change].cels:
var layer: Layer = Global.current_project.layers[layer_i]
if layer.visible:
var layer: BaseLayer = Global.current_project.layers[layer_i]
if layer.is_visible_in_hierarchy():
# Ignore layer if it has the "_io" suffix in its name (case in-sensitive)
if not (layer.name.to_lower().ends_with("_io")):
color.a = 0.6 / i

View file

@ -98,7 +98,7 @@ func _input(event: InputEvent) -> void:
if Global.cross_cursor:
cursor = Control.CURSOR_CROSS
var project: Project = Global.current_project
var layer: Layer = project.layers[project.current_layer]
var layer: BaseLayer = project.layers[project.current_layer]
if not layer.can_layer_get_drawn():
cursor = Control.CURSOR_FORBIDDEN
@ -644,7 +644,9 @@ func _get_selected_draw_images() -> Array: # Array of Images
var images := []
var project: Project = Global.current_project
for cel_index in project.selected_cels:
var cel: Cel = project.frames[cel_index[0]].cels[cel_index[1]]
var cel: BaseCel = project.frames[cel_index[0]].cels[cel_index[1]]
if not cel is PixelCel:
continue
if project.layers[cel_index[1]].can_layer_get_drawn():
images.append(cel.image)
return images
@ -665,7 +667,7 @@ func copy() -> void:
var cl_big_bounding_rectangle := Rect2()
var cl_selection_offset := Vector2.ZERO
var image: Image = project.frames[project.current_frame].cels[project.current_layer].image
var image: Image = project.frames[project.current_frame].cels[project.current_layer].get_image()
var to_copy := Image.new()
if !project.has_selection:
to_copy.copy_from(image)

View file

@ -108,10 +108,9 @@ func _on_CreateNewImage_confirmed() -> void:
proj_name = tr("untitled")
var new_project := Project.new([], proj_name, Vector2(width, height).floor())
new_project.layers.append(Layer.new())
new_project.layers.append(PixelLayer.new(new_project))
new_project.fill_color = fill_color
var frame: Frame = new_project.new_empty_frame()
new_project.frames.append(frame)
new_project.frames.append(new_project.new_empty_frame())
Global.projects.append(new_project)
Global.tabs.current_tab = Global.tabs.get_tab_count() - 1
Global.canvas.camera_zoom()

View file

@ -22,7 +22,7 @@ func _on_ResizeCanvas_about_to_show() -> void:
var layer_i := 0
for cel in Global.current_project.frames[Global.current_project.current_frame].cels:
if Global.current_project.layers[layer_i].visible:
if cel is PixelCel and Global.current_project.layers[layer_i].is_visible_in_hierarchy():
var cel_image := Image.new()
cel_image.copy_from(cel.image)
cel_image.lock()

View file

@ -5,7 +5,7 @@ enum ImageImportOptions {
SPRITESHEET_TAB,
SPRITESHEET_LAYER,
NEW_FRAME,
REPLACE_FRAME,
REPLACE_CEL,
NEW_LAYER,
PALETTE,
BRUSH,
@ -29,7 +29,7 @@ onready var frame_size_label: Label = $VBoxContainer/SizeContainer/FrameSizeLabe
onready var spritesheet_tab_options = $VBoxContainer/HBoxContainer/SpritesheetTabOptions
onready var spritesheet_lay_opt = $VBoxContainer/HBoxContainer/SpritesheetLayerOptions
onready var new_frame_options = $VBoxContainer/HBoxContainer/NewFrameOptions
onready var replace_frame_options = $VBoxContainer/HBoxContainer/ReplaceFrameOptions
onready var replace_cel_options = $VBoxContainer/HBoxContainer/ReplaceCelOptions
onready var new_layer_options = $VBoxContainer/HBoxContainer/NewLayerOptions
onready var new_brush_options = $VBoxContainer/HBoxContainer/NewBrushOptions
onready var new_brush_name = $VBoxContainer/HBoxContainer/NewBrushOptions/BrushName
@ -47,7 +47,7 @@ func _on_PreviewDialog_about_to_show() -> void:
import_options.add_item("Spritesheet (new project)")
import_options.add_item("Spritesheet (new layer)")
import_options.add_item("New frame")
import_options.add_item("Replace frame")
import_options.add_item("Replace cel")
import_options.add_item("New layer")
import_options.add_item("New palette")
import_options.add_item("New brush")
@ -129,13 +129,13 @@ func _on_PreviewDialog_confirmed() -> void:
)
elif current_import_option == ImageImportOptions.NEW_FRAME:
var layer_index: int = new_frame_options.get_node("AtLayerSpinbox").value
var layer_index: int = new_frame_options.get_node("AtLayerOption").get_selected_id()
OpenSave.open_image_as_new_frame(image, layer_index)
elif current_import_option == ImageImportOptions.REPLACE_FRAME:
var layer_index: int = replace_frame_options.get_node("AtLayerSpinbox").value
var frame_index: int = replace_frame_options.get_node("AtFrameSpinbox").value - 1
OpenSave.open_image_at_frame(image, layer_index, frame_index)
elif current_import_option == ImageImportOptions.REPLACE_CEL:
var layer_index: int = replace_cel_options.get_node("AtLayerOption").get_selected_id()
var frame_index: int = replace_cel_options.get_node("AtFrameSpinbox").value - 1
OpenSave.open_image_at_cel(image, layer_index, frame_index)
elif current_import_option == ImageImportOptions.NEW_LAYER:
var frame_index: int = new_layer_options.get_node("AtFrameSpinbox").value - 1
@ -204,15 +204,15 @@ func synchronize() -> void:
).value)
elif id == ImageImportOptions.NEW_FRAME:
dialog.new_frame_options.get_node("AtLayerSpinbox").value = (new_frame_options.get_node(
"AtLayerSpinbox"
).value)
dialog.new_frame_options.get_node("AtLayerOption").selected = (new_frame_options.get_node(
"AtLayerOption"
).selected)
elif id == ImageImportOptions.REPLACE_FRAME:
dialog.replace_frame_options.get_node("AtLayerSpinbox").value = (replace_frame_options.get_node(
"AtLayerSpinbox"
).value)
dialog.replace_frame_options.get_node("AtFrameSpinbox").value = (replace_frame_options.get_node(
elif id == ImageImportOptions.REPLACE_CEL:
dialog.replace_cel_options.get_node("AtLayerOption").selected = (replace_cel_options.get_node(
"AtLayerOption"
).selected)
dialog.replace_cel_options.get_node("AtFrameSpinbox").value = (replace_cel_options.get_node(
"AtFrameSpinbox"
).value)
@ -236,7 +236,7 @@ func _on_ImportOption_item_selected(id: int) -> void:
spritesheet_tab_options.visible = false
spritesheet_lay_opt.visible = false
new_frame_options.visible = false
replace_frame_options.visible = false
replace_cel_options.visible = false
new_layer_options.visible = false
new_brush_options.visible = false
texture_rect.get_child(0).visible = false
@ -260,18 +260,33 @@ func _on_ImportOption_item_selected(id: int) -> void:
elif id == ImageImportOptions.NEW_FRAME:
new_frame_options.visible = true
new_frame_options.get_node("AtLayerSpinbox").max_value = (
Global.current_project.layers.size()
- 1
)
elif id == ImageImportOptions.REPLACE_FRAME:
replace_frame_options.visible = true
replace_frame_options.get_node("AtLayerSpinbox").max_value = (
Global.current_project.layers.size()
- 1
)
var at_frame_spinbox: SpinBox = replace_frame_options.get_node("AtFrameSpinbox")
# Fill the at layer option button:
var at_layer_option: OptionButton = new_frame_options.get_node("AtLayerOption")
var layers := Global.current_project.layers.duplicate()
layers.invert()
var i := 0
for l in layers:
if not l is PixelLayer:
continue
at_layer_option.add_item(l.name, l.index)
at_layer_option.set_item_tooltip(i, l.get_layer_path())
i += 1
at_layer_option.selected = at_layer_option.get_item_count() - 1
elif id == ImageImportOptions.REPLACE_CEL:
replace_cel_options.visible = true
# Fill the at layer option button:
var at_layer_option: OptionButton = replace_cel_options.get_node("AtLayerOption")
var layers := Global.current_project.layers.duplicate()
layers.invert()
var i := 0
for l in layers:
if not l is PixelLayer:
continue
at_layer_option.add_item(l.name, l.index)
at_layer_option.set_item_tooltip(i, l.get_layer_path())
i += 1
at_layer_option.selected = at_layer_option.get_item_count() - 1
var at_frame_spinbox: SpinBox = replace_cel_options.get_node("AtFrameSpinbox")
at_frame_spinbox.max_value = Global.current_project.frames.size()
elif id == ImageImportOptions.NEW_LAYER:

View file

@ -161,40 +161,40 @@ margin_right = 53.0
margin_bottom = 19.0
text = "At layer:"
[node name="AtLayerSpinbox" type="SpinBox" parent="VBoxContainer/HBoxContainer/NewFrameOptions"]
[node name="AtLayerOption" type="OptionButton" parent="VBoxContainer/HBoxContainer/NewFrameOptions"]
margin_left = 57.0
margin_right = 131.0
margin_bottom = 24.0
margin_right = 86.0
margin_bottom = 20.0
mouse_default_cursor_shape = 2
max_value = 0.0
align = 2
[node name="ReplaceFrameOptions" type="HBoxContainer" parent="VBoxContainer/HBoxContainer"]
[node name="ReplaceCelOptions" type="HBoxContainer" parent="VBoxContainer/HBoxContainer"]
visible = false
margin_left = 155.0
margin_right = 427.0
margin_bottom = 24.0
[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/ReplaceFrameOptions"]
[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/ReplaceCelOptions"]
margin_top = 5.0
margin_right = 53.0
margin_bottom = 19.0
text = "At layer:"
[node name="AtLayerSpinbox" type="SpinBox" parent="VBoxContainer/HBoxContainer/ReplaceFrameOptions"]
[node name="AtLayerOption" type="OptionButton" parent="VBoxContainer/HBoxContainer/ReplaceCelOptions"]
margin_left = 57.0
margin_right = 131.0
margin_bottom = 24.0
margin_right = 86.0
margin_bottom = 20.0
mouse_default_cursor_shape = 2
max_value = 0.0
align = 2
[node name="Label2" type="Label" parent="VBoxContainer/HBoxContainer/ReplaceFrameOptions"]
[node name="Label2" type="Label" parent="VBoxContainer/HBoxContainer/ReplaceCelOptions"]
margin_left = 135.0
margin_top = 5.0
margin_right = 194.0
margin_bottom = 19.0
text = "At frame:"
[node name="AtFrameSpinbox" type="SpinBox" parent="VBoxContainer/HBoxContainer/ReplaceFrameOptions"]
[node name="AtFrameSpinbox" type="SpinBox" parent="VBoxContainer/HBoxContainer/ReplaceCelOptions"]
margin_left = 198.0
margin_right = 272.0
margin_bottom = 24.0

View file

@ -12,6 +12,8 @@ var max_cel_size := 144
var past_above_canvas := true
var future_above_canvas := true
var frame_button_node = preload("res://src/UI/Timeline/FrameButton.tscn")
onready var old_scroll: int = 0 # The previous scroll state of $ScrollContainer
onready var tag_spacer = find_node("TagSpacer")
onready var start_spacer = find_node("StartSpacer")
@ -23,6 +25,7 @@ onready var tag_scroll_container: ScrollContainer = find_node("TagScroll")
onready var fps_spinbox: SpinBox = find_node("FPSValue")
onready var onion_skinning_button: BaseButton = find_node("OnionSkinning")
onready var loop_animation_button: BaseButton = find_node("LoopAnim")
onready var drag_highlight: ColorRect = find_node("DragHighlight")
func _ready() -> void:
@ -36,6 +39,11 @@ func _ready() -> void:
timeline_scroll.size_flags_horizontal = SIZE_FILL
func _notification(what: int) -> void:
if what == NOTIFICATION_DRAG_END:
drag_highlight.hide()
func _input(event: InputEvent) -> void:
var mouse_pos := get_global_mouse_position()
var timeline_rect := Rect2(rect_global_position, rect_size)
@ -125,18 +133,16 @@ func add_frame() -> void:
var project: Project = Global.current_project
var frame_add_index := project.current_frame + 1
var frame: Frame = project.new_empty_frame()
var new_frames: Array = project.frames.duplicate()
var new_layers: Array = project.duplicate_layers()
new_frames.insert(frame_add_index, frame)
for l_i in range(new_layers.size()):
if new_layers[l_i].new_cels_linked: # If the link button is pressed
if new_layers[l_i].get("new_cels_linked"): # If the link button is pressed
new_layers[l_i].linked_cels.append(frame)
frame.cels[l_i].image = new_layers[l_i].linked_cels[0].cels[l_i].image
frame.cels[l_i].set_content(new_layers[l_i].linked_cels[0].cels[l_i].get_content())
frame.cels[l_i].image_texture = new_layers[l_i].linked_cels[0].cels[l_i].image_texture
# Code to PUSH AHEAD tags starting after the frame
var new_animation_tags := Global.current_project.animation_tags.duplicate()
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 Global.current_project.animation_tags's classes. Needed for undo/redo to work properly.
for i in new_animation_tags.size():
@ -158,46 +164,43 @@ func add_frame() -> void:
project.undo_redo.create_action("Add Frame")
project.undo_redo.add_do_method(Global, "undo_or_redo", false)
project.undo_redo.add_undo_method(Global, "undo_or_redo", true)
project.undo_redo.add_do_property(project, "frames", new_frames)
project.undo_redo.add_do_property(project, "current_frame", project.current_frame + 1)
Global.current_project.undo_redo.add_do_property(
Global.current_project, "animation_tags", new_animation_tags
)
project.undo_redo.add_do_property(project, "layers", new_layers)
project.undo_redo.add_undo_property(project, "frames", project.frames)
project.undo_redo.add_undo_property(project, "current_frame", project.current_frame)
Global.current_project.undo_redo.add_undo_property(
Global.current_project, "animation_tags", Global.current_project.animation_tags
)
project.undo_redo.add_undo_property(project, "layers", project.layers)
project.undo_redo.add_do_method(project, "add_frames", [frame], [frame_add_index])
project.undo_redo.add_undo_method(project, "remove_frames", [frame_add_index])
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_property(project, "current_frame", project.current_frame + 1)
project.undo_redo.add_undo_property(project, "current_frame", project.current_frame)
project.undo_redo.commit_action()
func _on_DeleteFrame_pressed(frame := -1) -> void:
var frames := []
func _on_DeleteFrame_pressed() -> void:
var indices := []
for cel in Global.current_project.selected_cels:
frame = cel[0]
if not frame in frames:
frames.append(frame)
frames.sort()
delete_frames(frames)
var f: int = cel[0]
if not f in indices:
indices.append(f)
indices.sort()
delete_frames(indices)
func delete_frames(frames := []) -> void:
if Global.current_project.frames.size() == 1:
func delete_frames(indices := []) -> void:
var project: Project = Global.current_project
if project.frames.size() == 1:
return
if frames.size() == 0:
frames.append(Global.current_project.current_frame)
if indices.size() == project.frames.size():
indices.remove(indices.size() - 1) # Ensure the project has at least 1 frame
elif indices.size() == 0:
indices.append(project.current_frame)
var new_frames: Array = Global.current_project.frames.duplicate()
var current_frame := Global.current_project.current_frame
var new_layers: Array = Global.current_project.duplicate_layers()
var current_frame: int = min(project.current_frame, project.frames.size() - indices.size() - 1)
var new_layers: Array = project.duplicate_layers()
var frames := []
var frame_correction := 0 # Only needed for tag adjustment
var new_animation_tags := Global.current_project.animation_tags.duplicate()
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 Global.current_project.animation_tags's classes. Needed for undo/redo to work properly.
for i in new_animation_tags.size():
@ -208,90 +211,70 @@ func delete_frames(frames := []) -> void:
new_animation_tags[i].to
)
for frame in frames:
if new_frames.size() == 1: # If only 1 frame
break
var frame_to_delete: Frame = Global.current_project.frames[frame]
new_frames.erase(frame_to_delete)
if current_frame > 0 && current_frame == new_frames.size(): # If it's the last frame
current_frame -= 1
for f in indices:
frames.append(project.frames[f])
# Check if one of the cels of the frame is linked
# if they are, unlink them too
# this prevents removed cels being kept in linked memory
for layer in new_layers:
for linked in layer.linked_cels:
if linked == Global.current_project.frames[frame]:
if linked == project.frames[f]:
layer.linked_cels.erase(linked)
# Loop through the tags to see if the frame is in one
frame -= frame_correction # Erasing made frames indexes 1 step ahead their intended tags
f -= frame_correction # Erasing made frames indexes 1 step ahead their intended tags
var tag_correction := 0 # needed when tag is erased
for tag_ind in new_animation_tags.size():
var tag = new_animation_tags[tag_ind - tag_correction]
if frame + 1 >= tag.from && frame + 1 <= tag.to:
if f + 1 >= tag.from && f + 1 <= tag.to:
if tag.from == tag.to: # If we're deleting the only frame in the tag
new_animation_tags.erase(tag)
tag_correction += 1
else:
tag.to -= 1
elif frame + 1 < tag.from:
elif f + 1 < tag.from:
tag.from -= 1
tag.to -= 1
frame_correction += 1 # Compensation for the next batch
Global.current_project.undos += 1
Global.current_project.undo_redo.create_action("Remove Frame")
Global.current_project.undo_redo.add_do_property(Global.current_project, "frames", new_frames)
Global.current_project.undo_redo.add_do_property(
Global.current_project, "current_frame", current_frame
)
Global.current_project.undo_redo.add_do_property(
Global.current_project, "animation_tags", new_animation_tags
)
Global.current_project.undo_redo.add_do_property(Global.current_project, "layers", new_layers)
Global.current_project.undo_redo.add_undo_property(
Global.current_project, "frames", Global.current_project.frames
)
Global.current_project.undo_redo.add_undo_property(
Global.current_project, "current_frame", Global.current_project.current_frame
)
Global.current_project.undo_redo.add_undo_property(
Global.current_project, "animation_tags", Global.current_project.animation_tags
)
Global.current_project.undo_redo.add_undo_property(
Global.current_project, "layers", Global.current_project.layers
)
Global.current_project.undo_redo.add_do_method(Global, "undo_or_redo", false)
Global.current_project.undo_redo.add_undo_method(Global, "undo_or_redo", true)
Global.current_project.undo_redo.commit_action()
project.undos += 1
project.undo_redo.create_action("Remove Frame")
project.undo_redo.add_do_property(project, "layers", new_layers)
project.undo_redo.add_undo_property(project, "layers", Global.current_project.layers)
project.undo_redo.add_do_method(project, "remove_frames", indices)
project.undo_redo.add_undo_method(project, "add_frames", frames, indices)
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_property(project, "current_frame", current_frame)
project.undo_redo.add_undo_property(project, "current_frame", project.current_frame)
project.undo_redo.add_do_method(Global, "undo_or_redo", false)
project.undo_redo.add_undo_method(Global, "undo_or_redo", true)
project.undo_redo.commit_action()
func _on_CopyFrame_pressed(frame := -1) -> void:
var frames := []
func _on_CopyFrame_pressed() -> void:
var indices := []
for cel in Global.current_project.selected_cels:
frame = cel[0]
if not frame in frames:
frames.append(frame)
frames.sort()
copy_frames(frames)
var f: int = cel[0]
if not f in indices:
indices.append(f)
indices.sort()
copy_frames(indices)
func copy_frames(frames := []) -> void:
Global.canvas.selection.transform_content_confirm()
func copy_frames(indices := []) -> void:
var project: Project = Global.current_project
if frames.size() == 0:
frames.append(Global.current_project.current_frame)
if indices.size() == 0:
indices.append(project.current_frame)
var new_frames := Global.current_project.frames.duplicate()
var new_layers: Array = Global.current_project.duplicate_layers()
var new_layers: Array = project.duplicate_layers()
var copied_frames := []
var copied_indices := range(indices[-1] + 1, indices[-1] + 1 + indices.size())
var new_animation_tags := Global.current_project.animation_tags.duplicate()
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 Global.current_project.animation_tags's classes. Needed for undo/redo to work properly.
# 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,
@ -300,61 +283,48 @@ func copy_frames(frames := []) -> void:
new_animation_tags[i].to
)
for frm in frames.size():
var frame = frames[(frames.size() - 1) - frm]
for f in indices:
var new_frame := Frame.new()
new_frames.insert(frames[-1] + 1, new_frame)
copied_frames.append(new_frame)
var prev_frame: Frame = Global.current_project.frames[frame]
for cel in prev_frame.cels: # Copy every cel
var sprite := Image.new()
sprite.copy_from(cel.image)
var sprite_texture := ImageTexture.new()
sprite_texture.create_from_image(sprite, 0)
new_frame.cels.append(Cel.new(sprite, cel.opacity, sprite_texture))
var prev_frame: Frame = project.frames[f]
new_frame.duration = prev_frame.duration
for l_i in range(new_layers.size()):
if new_layers[l_i].new_cels_linked: # If the link button is pressed
# If the layer has new_cels_linked variable, and its true
var new_cels_linked := true if new_layers[l_i].get("new_cels_linked") else false
# Copy the cel, create new cel content if new cels aren't linked
new_frame.cels.append(new_layers[l_i].copy_cel(f, new_cels_linked))
if new_cels_linked: # If the link button is pressed
new_layers[l_i].linked_cels.append(new_frame)
new_frame.cels[l_i].image = new_layers[l_i].linked_cels[0].cels[l_i].image
new_frame.cels[l_i].set_content(
new_layers[l_i].linked_cels[0].cels[l_i].get_content()
)
new_frame.cels[l_i].image_texture = new_layers[l_i].linked_cels[0].cels[l_i].image_texture
# Loop through the tags to see if the frame is in one
for tag in new_animation_tags:
if frames[-1] + 1 >= tag.from && frames[-1] + 1 <= tag.to:
if indices[-1] + 1 >= tag.from && indices[-1] + 1 <= tag.to:
tag.to += 1
elif frames[-1] + 1 < tag.from:
elif indices[-1] + 1 < tag.from:
tag.from += 1
tag.to += 1
Global.current_project.undos += 1
Global.current_project.undo_redo.create_action("Add Frame")
Global.current_project.undo_redo.add_do_method(Global, "undo_or_redo", false)
Global.current_project.undo_redo.add_undo_method(Global, "undo_or_redo", true)
Global.current_project.undo_redo.add_do_property(Global.current_project, "frames", new_frames)
Global.current_project.undo_redo.add_do_property(
Global.current_project, "current_frame", frames[-1] + 1
)
Global.current_project.undo_redo.add_do_property(Global.current_project, "layers", new_layers)
Global.current_project.undo_redo.add_do_property(
Global.current_project, "animation_tags", new_animation_tags
)
Global.current_project.undo_redo.add_undo_property(
Global.current_project, "frames", Global.current_project.frames
)
Global.current_project.undo_redo.add_undo_property(
Global.current_project, "current_frame", frames[-1]
)
Global.current_project.undo_redo.add_undo_property(
Global.current_project, "layers", Global.current_project.layers
)
Global.current_project.undo_redo.add_undo_property(
Global.current_project, "animation_tags", Global.current_project.animation_tags
)
Global.current_project.undo_redo.commit_action()
project.undos += 1
project.undo_redo.create_action("Add Frame")
project.undo_redo.add_do_method(Global, "undo_or_redo", false)
project.undo_redo.add_undo_method(Global, "undo_or_redo", true)
project.undo_redo.add_do_property(project, "layers", new_layers)
project.undo_redo.add_undo_property(project, "layers", project.layers)
project.undo_redo.add_do_method(project, "add_frames", copied_frames, copied_indices)
project.undo_redo.add_undo_method(project, "remove_frames", copied_indices)
project.undo_redo.add_do_property(project, "current_frame", indices[-1] + 1)
project.undo_redo.add_undo_property(project, "current_frame", indices[-1])
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.commit_action()
func _on_FrameTagButton_pressed() -> void:
@ -431,9 +401,12 @@ func _on_PlayBackwards_toggled(button_pressed: bool) -> void:
play_animation(button_pressed, false)
# Called on each frame of the animation
func _on_AnimationTimer_timeout() -> void:
if first_frame == last_frame:
$AnimationTimer.stop()
Global.play_forward.pressed = false
Global.play_backwards.pressed = false
Global.animation_timer.stop()
return
Global.canvas.selection.transform_content_confirm()
@ -597,202 +570,237 @@ func _on_FuturePlacement_item_selected(index: int) -> void:
# Layer buttons
func add_layer(is_new := true) -> void:
Global.canvas.selection.transform_content_confirm()
var new_layers: Array = Global.current_project.layers.duplicate()
var l := Layer.new()
if !is_new: # Clone layer
l.name = (
Global.current_project.layers[Global.current_project.current_layer].name
+ " ("
+ tr("copy")
+ ")"
)
new_layers.append(l)
func _on_AddLayer_pressed() -> void:
var project: Project = Global.current_project
Global.current_project.undos += 1
Global.current_project.undo_redo.create_action("Add Layer")
var l := PixelLayer.new(project)
var cels := []
for f in project.frames:
cels.append(l.new_empty_cel())
for f in Global.current_project.frames:
var new_layer := Image.new()
if is_new:
new_layer.create(
Global.current_project.size.x,
Global.current_project.size.y,
false,
Image.FORMAT_RGBA8
)
else: # Clone layer
new_layer.copy_from(f.cels[Global.current_project.current_layer].image)
project.undos += 1
project.undo_redo.create_action("Add Layer")
project.undo_redo.add_do_property(project, "current_layer", project.layers.size())
project.undo_redo.add_undo_property(project, "current_layer", project.current_layer)
project.undo_redo.add_do_method(project, "add_layers", [l], [project.layers.size()], [cels])
project.undo_redo.add_undo_method(project, "remove_layers", [project.layers.size()])
project.undo_redo.add_do_method(Global, "undo_or_redo", false)
project.undo_redo.add_undo_method(Global, "undo_or_redo", true)
project.undo_redo.commit_action()
var new_cels: Array = f.cels.duplicate()
new_cels.append(Cel.new(new_layer, 1))
Global.current_project.undo_redo.add_do_property(f, "cels", new_cels)
Global.current_project.undo_redo.add_undo_property(f, "cels", f.cels)
Global.current_project.undo_redo.add_do_property(
Global.current_project, "current_layer", Global.current_project.layers.size()
func _on_AddGroup_pressed() -> void:
var project: Project = Global.current_project
var l := GroupLayer.new(project)
var cels := []
for f in project.frames:
cels.append(l.new_empty_cel())
project.undos += 1
project.undo_redo.create_action("Add Layer")
project.undo_redo.add_do_property(project, "current_layer", project.layers.size())
project.undo_redo.add_undo_property(project, "current_layer", project.current_layer)
project.undo_redo.add_do_method(project, "add_layers", [l], [project.layers.size()], [cels])
project.undo_redo.add_undo_method(project, "remove_layers", [project.layers.size()])
project.undo_redo.add_do_method(Global, "undo_or_redo", false)
project.undo_redo.add_undo_method(Global, "undo_or_redo", true)
project.undo_redo.commit_action()
func _on_CloneLayer_pressed() -> void:
var project: Project = Global.current_project
var source_layers: Array = project.layers[project.current_layer].get_children(true)
source_layers.append(project.layers[project.current_layer])
var clones := [] # Array of Layers
var cels := [] # 2D Array of Cels
for sl in source_layers:
var cl: BaseLayer = sl.copy()
if sl.index == project.current_layer:
cl.name = str(sl.name, " (", tr("copy"), ")")
clones.append(cl)
cels.append(sl.copy_all_cels())
# Swap parents with clones if the parent is one of the source layers
for cl in clones:
var p = source_layers.find(cl.parent)
if p > -1:
cl.parent = clones[p]
var indices := range(project.current_layer + 1, project.current_layer + clones.size() + 1)
project.undos += 1
project.undo_redo.create_action("Add Layer")
project.undo_redo.add_do_property(
project, "current_layer", project.current_layer + clones.size()
)
Global.current_project.undo_redo.add_do_property(Global.current_project, "layers", new_layers)
Global.current_project.undo_redo.add_undo_property(
Global.current_project, "current_layer", Global.current_project.current_layer
)
Global.current_project.undo_redo.add_undo_property(
Global.current_project, "layers", Global.current_project.layers
)
Global.current_project.undo_redo.add_undo_method(Global, "undo_or_redo", true)
Global.current_project.undo_redo.add_do_method(Global, "undo_or_redo", false)
Global.current_project.undo_redo.commit_action()
project.undo_redo.add_undo_property(project, "current_layer", project.current_layer)
project.undo_redo.add_do_method(project, "add_layers", clones, indices, cels)
project.undo_redo.add_undo_method(project, "remove_layers", indices)
project.undo_redo.add_do_method(Global, "undo_or_redo", false)
project.undo_redo.add_undo_method(Global, "undo_or_redo", true)
project.undo_redo.commit_action()
func _on_RemoveLayer_pressed() -> void:
if Global.current_project.layers.size() == 1:
var project: Project = Global.current_project
if project.layers.size() == 1:
return
var new_layers: Array = Global.current_project.layers.duplicate()
new_layers.remove(Global.current_project.current_layer)
Global.current_project.undos += 1
Global.current_project.undo_redo.create_action("Remove Layer")
if Global.current_project.current_layer > 0:
Global.current_project.undo_redo.add_do_property(
Global.current_project, "current_layer", Global.current_project.current_layer - 1
)
else:
Global.current_project.undo_redo.add_do_property(
Global.current_project, "current_layer", Global.current_project.current_layer
)
for f in Global.current_project.frames:
var new_cels: Array = f.cels.duplicate()
new_cels.remove(Global.current_project.current_layer)
Global.current_project.undo_redo.add_do_property(f, "cels", new_cels)
Global.current_project.undo_redo.add_undo_property(f, "cels", f.cels)
var layers: Array = project.layers[project.current_layer].get_children(true)
layers.append(project.layers[project.current_layer])
var indices := []
for l in layers:
indices.append(l.index)
Global.current_project.undo_redo.add_do_property(Global.current_project, "layers", new_layers)
Global.current_project.undo_redo.add_undo_property(
Global.current_project, "current_layer", Global.current_project.current_layer
var cels := []
for l in layers:
cels.append([])
for f in project.frames:
cels[-1].append(f.cels[l.index])
project.undos += 1
project.undo_redo.create_action("Remove Layer")
project.undo_redo.add_do_property(project, "current_layer", max(indices[0] - 1, 0))
project.undo_redo.add_undo_property(project, "current_layer", project.current_layer)
project.undo_redo.add_do_method(project, "remove_layers", indices)
project.undo_redo.add_undo_method(project, "add_layers", layers, indices, cels)
project.undo_redo.add_do_method(Global, "undo_or_redo", false)
project.undo_redo.add_undo_method(Global, "undo_or_redo", true)
project.undo_redo.commit_action()
# Move the layer up or down in layer order and/or reparent to be deeper/shallower in the
# layer hierarchy depending on its current index and parent
func change_layer_order(up: bool) -> void:
var project: Project = Global.current_project
var layer: BaseLayer = project.layers[project.current_layer]
var child_count = layer.get_child_count(true)
var from_indices := range(layer.index - child_count, layer.index + 1)
var from_parents := []
for l in from_indices:
from_parents.append(project.layers[l].parent)
var to_parents := from_parents.duplicate()
var to_index = layer.index - child_count # the index where the LOWEST shifted layer should end up
if up:
var above_layer: BaseLayer = project.layers[project.current_layer + 1]
if layer.parent == above_layer: # Above is the parent, leave the parent and go up
to_parents[-1] = above_layer.parent
to_index = to_index + 1
elif layer.parent != above_layer.parent: # Above layer must be deeper in the hierarchy
# Move layer 1 level deeper in hierarchy. Done by setting its parent to the parent of
# above_layer, and if that is multiple levels, drop levels until its just 1
to_parents[-1] = above_layer.parent
while to_parents[-1].parent != layer.parent:
to_parents[-1] = to_parents[-1].parent
elif above_layer.accepts_child(layer):
to_parents[-1] = above_layer
else:
to_index = to_index + 1
else: # Down
if layer.index == child_count: # If at the very bottom of the layer stack
if not is_instance_valid(layer.parent):
return
to_parents[-1] = layer.parent.parent # Drop a level in the hierarchy
else:
var below_layer: BaseLayer = project.layers[project.current_layer - 1 - child_count]
if layer.parent != below_layer.parent: # If there is a hierarchy change
to_parents[-1] = layer.parent.parent # Drop a level in the hierarchy
elif below_layer.accepts_child(layer):
to_parents[-1] = below_layer
to_index = to_index - 1
else:
to_index = to_index - 1
var to_indices := range(to_index, to_index + child_count + 1)
project.undo_redo.create_action("Change Layer Order")
project.undo_redo.add_do_property(project, "current_layer", to_index + child_count)
project.undo_redo.add_undo_property(project, "current_layer", project.current_layer)
project.undo_redo.add_do_method(project, "move_layers", from_indices, to_indices, to_parents)
project.undo_redo.add_undo_method(
project, "move_layers", to_indices, from_indices, from_parents
)
Global.current_project.undo_redo.add_undo_property(
Global.current_project, "layers", Global.current_project.layers
)
Global.current_project.undo_redo.add_do_method(Global, "undo_or_redo", false)
Global.current_project.undo_redo.add_undo_method(Global, "undo_or_redo", true)
Global.current_project.undo_redo.commit_action()
func change_layer_order(rate: int) -> void:
var change = Global.current_project.current_layer + rate
var new_layers: Array = Global.current_project.layers.duplicate()
var temp = new_layers[Global.current_project.current_layer]
new_layers[Global.current_project.current_layer] = new_layers[change]
new_layers[change] = temp
Global.current_project.undo_redo.create_action("Change Layer Order")
for f in Global.current_project.frames:
var new_cels: Array = f.cels.duplicate()
var temp_canvas = new_cels[Global.current_project.current_layer]
new_cels[Global.current_project.current_layer] = new_cels[change]
new_cels[change] = temp_canvas
Global.current_project.undo_redo.add_do_property(f, "cels", new_cels)
Global.current_project.undo_redo.add_undo_property(f, "cels", f.cels)
Global.current_project.undo_redo.add_do_property(
Global.current_project, "current_layer", change
)
Global.current_project.undo_redo.add_do_property(Global.current_project, "layers", new_layers)
Global.current_project.undo_redo.add_undo_property(
Global.current_project, "layers", Global.current_project.layers
)
Global.current_project.undo_redo.add_undo_property(
Global.current_project, "current_layer", Global.current_project.current_layer
)
Global.current_project.undo_redo.add_undo_method(Global, "undo_or_redo", true)
Global.current_project.undo_redo.add_do_method(Global, "undo_or_redo", false)
Global.current_project.undo_redo.commit_action()
project.undo_redo.add_do_method(Global, "undo_or_redo", false)
project.undo_redo.add_undo_method(Global, "undo_or_redo", true)
project.undo_redo.commit_action()
func _on_MergeDownLayer_pressed() -> void:
var new_layers: Array = Global.current_project.duplicate_layers()
var project: Project = Global.current_project
var top_layer: PixelLayer = project.layers[project.current_layer]
var bottom_layer: PixelLayer = project.layers[project.current_layer - 1]
var new_linked_cels: Array = bottom_layer.linked_cels.duplicate()
Global.current_project.undos += 1
Global.current_project.undo_redo.create_action("Merge Layer")
for f in Global.current_project.frames:
var new_cels: Array = f.cels.duplicate()
for i in new_cels.size():
new_cels[i] = Cel.new(new_cels[i].image, new_cels[i].opacity)
var selected_layer := Image.new()
selected_layer.copy_from(new_cels[Global.current_project.current_layer].image)
project.undos += 1
project.undo_redo.create_action("Merge Layer")
selected_layer.lock()
if f.cels[Global.current_project.current_layer].opacity < 1: # If we have layer transparency
for xx in selected_layer.get_size().x:
for yy in selected_layer.get_size().y:
var pixel_color: Color = selected_layer.get_pixel(xx, yy)
var alpha: float = (
pixel_color.a
* f.cels[Global.current_project.current_layer].opacity
)
selected_layer.set_pixel(
for f in project.frames:
var top_image := Image.new()
top_image.copy_from(f.cels[top_layer.index].image)
top_image.lock()
if f.cels[top_layer.index].opacity < 1: # If we have layer transparency
for xx in top_image.get_size().x:
for yy in top_image.get_size().y:
var pixel_color: Color = top_image.get_pixel(xx, yy)
var alpha: float = pixel_color.a * f.cels[top_layer.index].opacity
top_image.set_pixel(
xx, yy, Color(pixel_color.r, pixel_color.g, pixel_color.b, alpha)
)
selected_layer.unlock()
top_image.unlock()
var new_layer := Image.new()
new_layer.copy_from(f.cels[Global.current_project.current_layer - 1].image)
new_layer.blend_rect(
selected_layer, Rect2(Vector2.ZERO, Global.current_project.size), Vector2.ZERO
)
new_cels.remove(Global.current_project.current_layer)
var bottom_image := Image.new()
bottom_image.copy_from(f.cels[bottom_layer.index].image)
bottom_image.blend_rect(top_image, Rect2(Vector2.ZERO, project.size), Vector2.ZERO)
if (
!selected_layer.is_invisible()
and (
Global.current_project.layers[Global.current_project.current_layer - 1].linked_cels.size()
> 1
)
and (
f
in Global.current_project.layers[(
Global.current_project.current_layer
- 1
)].linked_cels
)
!top_image.is_invisible()
and bottom_layer.linked_cels.size() > 1
and f in bottom_layer.linked_cels
):
new_layers[Global.current_project.current_layer - 1].linked_cels.erase(f)
new_cels[Global.current_project.current_layer - 1].image = new_layer
new_linked_cels.erase(f)
project.undo_redo.add_do_property(
f.cels[bottom_layer.index], "image_texture", ImageTexture.new()
)
project.undo_redo.add_undo_property(
f.cels[bottom_layer.index],
"image_texture",
f.cels[bottom_layer.index].image_texture
)
project.undo_redo.add_do_property(f.cels[bottom_layer.index], "image", bottom_image)
project.undo_redo.add_undo_property(
f.cels[bottom_layer.index], "image", f.cels[bottom_layer.index].image
)
else:
Global.current_project.undo_redo.add_do_property(
f.cels[Global.current_project.current_layer - 1].image, "data", new_layer.data
project.undo_redo.add_do_property(
f.cels[bottom_layer.index].image, "data", bottom_image.data
)
Global.current_project.undo_redo.add_undo_property(
f.cels[Global.current_project.current_layer - 1].image,
"data",
f.cels[Global.current_project.current_layer - 1].image.data
project.undo_redo.add_undo_property(
f.cels[bottom_layer.index].image, "data", f.cels[bottom_layer.index].image.data
)
Global.current_project.undo_redo.add_do_property(f, "cels", new_cels)
Global.current_project.undo_redo.add_undo_property(f, "cels", f.cels)
var top_cels := []
for f in project.frames:
top_cels.append(f.cels[top_layer.index])
new_layers.remove(Global.current_project.current_layer)
Global.current_project.undo_redo.add_do_property(
Global.current_project, "current_layer", Global.current_project.current_layer - 1
project.undo_redo.add_do_property(project, "current_layer", bottom_layer.index)
project.undo_redo.add_undo_property(project, "current_layer", top_layer.index)
project.undo_redo.add_do_property(bottom_layer, "linked_cels", new_linked_cels)
project.undo_redo.add_undo_property(bottom_layer, "linked_cels", bottom_layer.linked_cels)
project.undo_redo.add_do_method(project, "remove_layers", [top_layer.index])
project.undo_redo.add_undo_method(
project, "add_layers", [top_layer], [top_layer.index], [top_cels]
)
Global.current_project.undo_redo.add_do_property(Global.current_project, "layers", new_layers)
Global.current_project.undo_redo.add_undo_property(
Global.current_project, "layers", Global.current_project.layers
)
Global.current_project.undo_redo.add_undo_property(
Global.current_project, "current_layer", Global.current_project.current_layer
)
Global.current_project.undo_redo.add_undo_method(Global, "undo_or_redo", true)
Global.current_project.undo_redo.add_do_method(Global, "undo_or_redo", false)
Global.current_project.undo_redo.commit_action()
project.undo_redo.add_undo_method(Global, "undo_or_redo", true)
project.undo_redo.add_do_method(Global, "undo_or_redo", false)
project.undo_redo.commit_action()
func _on_OpacitySlider_value_changed(value) -> void:
var current_frame: Frame = Global.current_project.frames[Global.current_project.current_frame]
var cel: Cel = current_frame.cels[Global.current_project.current_layer]
var cel: BaseCel = current_frame.cels[Global.current_project.current_layer]
cel.opacity = value / 100
Global.layer_opacity_slider.value = value
Global.layer_opacity_spinbox.value = value
@ -801,3 +809,117 @@ func _on_OpacitySlider_value_changed(value) -> void:
func _on_OnionSkinningSettings_popup_hide() -> void:
Global.can_draw = true
# Methods to update the UI in response to changes in the current project
func project_changed() -> void:
var project: Project = Global.current_project
# These must be removed from tree immediately to not mess up the indices of
# the new buttons, so use either free or queue_free + parent.remove_child
for child in Global.layers_container.get_children():
child.free()
for child in Global.frame_ids.get_children():
child.free()
for container in Global.frames_container.get_children():
container.free()
for i in project.layers.size():
project_layer_added(i)
for f in project.frames.size():
var button: Button = frame_button_node.instance()
button.frame = f
Global.frame_ids.add_child(button)
# Press selected cel/frame/layer buttons
for cel in project.selected_cels:
var frame: int = cel[0]
var layer: int = cel[1]
if frame < Global.frame_ids.get_child_count():
var frame_button: BaseButton = Global.frame_ids.get_child(frame)
frame_button.pressed = true
var container_child_count: int = Global.frames_container.get_child_count()
if layer < container_child_count:
var container = Global.frames_container.get_child(container_child_count - 1 - layer)
if frame < container.get_child_count():
var cel_button = container.get_child(frame)
cel_button.pressed = true
var layer_button = Global.layers_container.get_child(container_child_count - 1 - layer)
layer_button.pressed = true
func project_frame_added(frame: int) -> void:
var project: Project = Global.current_project
var button: Button = frame_button_node.instance()
button.frame = frame
Global.frame_ids.add_child(button)
Global.frame_ids.move_child(button, frame)
var layer := Global.frames_container.get_child_count() - 1
for container in Global.frames_container.get_children():
var cel_button = project.frames[frame].cels[layer].instantiate_cel_button()
cel_button.frame = frame
cel_button.layer = layer
container.add_child(cel_button)
container.move_child(cel_button, frame)
layer -= 1
func project_frame_removed(frame: int) -> void:
Global.frame_ids.get_child(frame).queue_free()
Global.frame_ids.remove_child(Global.frame_ids.get_child(frame))
for container in Global.frames_container.get_children():
container.get_child(frame).free()
func project_layer_added(layer: int) -> void:
var project: Project = Global.current_project
var layer_button: LayerButton = project.layers[layer].instantiate_layer_button()
layer_button.layer = layer
if project.layers[layer].name == "":
project.layers[layer].set_name_to_default(layer)
var layer_cel_container := HBoxContainer.new()
for f in project.frames.size():
var cel_button = project.frames[f].cels[layer].instantiate_cel_button()
cel_button.frame = f
cel_button.layer = layer
layer_cel_container.add_child(cel_button)
layer_button.visible = Global.current_project.layers[layer].is_expanded_in_hierarchy()
layer_cel_container.visible = layer_button.visible
Global.layers_container.add_child(layer_button)
var count := Global.layers_container.get_child_count()
Global.layers_container.move_child(layer_button, count - 1 - layer)
Global.frames_container.add_child(layer_cel_container)
Global.frames_container.move_child(layer_cel_container, count - 1 - layer)
func project_layer_removed(layer: int) -> void:
var count := Global.layers_container.get_child_count()
Global.layers_container.get_child(count - 1 - layer).free()
Global.frames_container.get_child(count - 1 - layer).free()
func project_cel_added(frame: int, layer: int) -> void:
var container := Global.frames_container.get_child(
Global.frames_container.get_child_count() - 1 - layer
)
var cel_button = Global.current_project.frames[frame].cels[layer].instantiate_cel_button()
cel_button.frame = frame
cel_button.layer = layer
container.add_child(cel_button)
container.move_child(cel_button, frame)
func project_cel_removed(frame: int, layer: int) -> void:
var container := Global.frames_container.get_child(
Global.frames_container.get_child_count() - 1 - layer
)
container.get_child(frame).queue_free()
container.remove_child(container.get_child(frame))

View file

@ -8,7 +8,7 @@
[ext_resource path="res://assets/graphics/layers/delete.png" type="Texture" id=6]
[ext_resource path="res://assets/graphics/layers/clone.png" type="Texture" id=7]
[ext_resource path="res://assets/graphics/timeline/move_arrow.png" type="Texture" id=8]
[ext_resource path="res://src/UI/Timeline/FrameButton.tscn" type="PackedScene" id=9]
[ext_resource path="res://assets/graphics/layers/group_new.png" type="Texture" id=10]
[ext_resource path="res://assets/graphics/timeline/new_frame.png" type="Texture" id=19]
[ext_resource path="res://assets/graphics/timeline/remove_frame.png" type="Texture" id=20]
[ext_resource path="res://assets/graphics/timeline/go_to_first_frame.png" type="Texture" id=21]
@ -99,14 +99,17 @@ margin_bottom = 160.0
rect_min_size = Vector2( 36, 160 )
rect_clip_content = true
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="ScrollContainer" type="ScrollContainer" parent="."]
anchor_right = 1.0
anchor_bottom = 1.0
[node name="TimelineContainer" type="VBoxContainer" parent="ScrollContainer"]
margin_right = 902.0
margin_bottom = 160.0
margin_right = 745.0
margin_bottom = 104.0
size_flags_horizontal = 3
size_flags_vertical = 3
__meta__ = {
@ -119,14 +122,15 @@ margin_bottom = 38.0
size_flags_horizontal = 3
[node name="LayerButtonPanelContainer" type="PanelContainer" parent="ScrollContainer/TimelineContainer/TimelineButtons"]
margin_right = 186.0
margin_right = 190.0
margin_bottom = 38.0
rect_min_size = Vector2( 190, 0 )
custom_styles/panel = SubResource( 2 )
[node name="LayerButtons" type="HBoxContainer" parent="ScrollContainer/TimelineContainer/TimelineButtons/LayerButtonPanelContainer"]
margin_left = 4.5
margin_top = 3.0
margin_right = 181.5
margin_right = 185.5
margin_bottom = 25.0
size_flags_vertical = 0
custom_constants/separation = 9
@ -155,11 +159,36 @@ __meta__ = {
"_edit_use_anchors_": false
}
[node name="RemoveLayer" type="Button" parent="ScrollContainer/TimelineContainer/TimelineButtons/LayerButtonPanelContainer/LayerButtons" groups=["UIButtons"]]
[node name="AddGroup" type="Button" parent="ScrollContainer/TimelineContainer/TimelineButtons/LayerButtonPanelContainer/LayerButtons" groups=["UIButtons"]]
margin_left = 31.0
margin_right = 53.0
margin_bottom = 22.0
rect_min_size = Vector2( 22, 22 )
hint_tooltip = "Create a new group layer"
focus_mode = 0
mouse_default_cursor_shape = 2
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/TimelineContainer/TimelineButtons/LayerButtonPanelContainer/LayerButtons/AddGroup"]
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
margin_left = -11.0
margin_top = -11.0
margin_right = 11.0
margin_bottom = 11.0
size_flags_horizontal = 0
size_flags_vertical = 0
texture = ExtResource( 10 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="RemoveLayer" type="Button" parent="ScrollContainer/TimelineContainer/TimelineButtons/LayerButtonPanelContainer/LayerButtons" groups=["UIButtons"]]
margin_left = 62.0
margin_right = 84.0
margin_bottom = 22.0
rect_min_size = Vector2( 22, 22 )
hint_tooltip = "Remove current layer"
focus_mode = 0
mouse_default_cursor_shape = 8
@ -182,8 +211,8 @@ __meta__ = {
}
[node name="MoveUpLayer" type="Button" parent="ScrollContainer/TimelineContainer/TimelineButtons/LayerButtonPanelContainer/LayerButtons" groups=["UIButtons"]]
margin_left = 62.0
margin_right = 84.0
margin_left = 93.0
margin_right = 115.0
margin_bottom = 22.0
rect_min_size = Vector2( 22, 22 )
hint_tooltip = "Move up the current layer"
@ -208,8 +237,8 @@ __meta__ = {
}
[node name="MoveDownLayer" type="Button" parent="ScrollContainer/TimelineContainer/TimelineButtons/LayerButtonPanelContainer/LayerButtons" groups=["UIButtons"]]
margin_left = 93.0
margin_right = 115.0
margin_left = 124.0
margin_right = 146.0
margin_bottom = 22.0
rect_min_size = Vector2( 22, 22 )
hint_tooltip = "Move down the current layer"
@ -234,8 +263,8 @@ __meta__ = {
}
[node name="CloneLayer" type="Button" parent="ScrollContainer/TimelineContainer/TimelineButtons/LayerButtonPanelContainer/LayerButtons" groups=["UIButtons"]]
margin_left = 124.0
margin_right = 146.0
margin_left = 155.0
margin_right = 177.0
margin_bottom = 22.0
rect_min_size = Vector2( 22, 22 )
hint_tooltip = "Clone current layer"
@ -259,8 +288,8 @@ __meta__ = {
}
[node name="MergeDownLayer" type="Button" parent="ScrollContainer/TimelineContainer/TimelineButtons/LayerButtonPanelContainer/LayerButtons" groups=["UIButtons"]]
margin_left = 155.0
margin_right = 177.0
margin_left = 186.0
margin_right = 208.0
margin_bottom = 22.0
rect_min_size = Vector2( 22, 22 )
hint_tooltip = "Merge current layer with the one below"
@ -285,8 +314,8 @@ __meta__ = {
}
[node name="Control" type="Control" parent="ScrollContainer/TimelineContainer/TimelineButtons"]
margin_left = 190.0
margin_right = 378.0
margin_left = 194.0
margin_right = 386.0
margin_bottom = 38.0
size_flags_horizontal = 3
@ -783,12 +812,12 @@ align = 1
[node name="SpacerControl2" type="Control" parent="ScrollContainer/TimelineContainer/OpacityAndTagContainer"]
margin_left = 188.0
margin_right = 217.0
margin_right = 240.0
margin_bottom = 32.0
rect_min_size = Vector2( 29, 0 )
rect_min_size = Vector2( 52, 32 )
[node name="TagScroll" type="ScrollContainer" parent="ScrollContainer/TimelineContainer/OpacityAndTagContainer"]
margin_left = 219.0
margin_left = 242.0
margin_right = 902.0
margin_bottom = 32.0
rect_min_size = Vector2( 0, 32 )
@ -798,7 +827,6 @@ theme = SubResource( 20 )
scroll_vertical_enabled = false
[node name="HBoxContainer" type="HBoxContainer" parent="ScrollContainer/TimelineContainer/OpacityAndTagContainer/TagScroll"]
margin_right = 683.0
margin_bottom = 32.0
size_flags_horizontal = 3
size_flags_vertical = 3
@ -808,7 +836,7 @@ custom_constants/separation = 0
margin_bottom = 32.0
[node name="TagContainer" type="Control" parent="ScrollContainer/TimelineContainer/OpacityAndTagContainer/TagScroll/HBoxContainer"]
margin_right = 683.0
margin_right = 660.0
margin_bottom = 32.0
size_flags_horizontal = 3
@ -835,8 +863,8 @@ margin_bottom = 68.0
size_flags_horizontal = 3
[node name="LayersAndFrames" type="HBoxContainer" parent="ScrollContainer/TimelineContainer/PanelContainer/HBoxContainer/TimelineScroll"]
margin_right = 81.0
margin_bottom = 68.0
margin_right = 45.0
margin_bottom = 20.0
size_flags_vertical = 3
[node name="LayerVBoxCont" type="VBoxContainer" parent="ScrollContainer/TimelineContainer/PanelContainer/HBoxContainer/TimelineScroll/LayersAndFrames"]
@ -858,22 +886,16 @@ margin_bottom = 20.0
[node name="FrameButtonsAndIds" type="VBoxContainer" parent="ScrollContainer/TimelineContainer/PanelContainer/HBoxContainer/TimelineScroll/LayersAndFrames"]
margin_left = 45.0
margin_right = 81.0
margin_right = 45.0
margin_bottom = 68.0
[node name="FrameIDs" type="HBoxContainer" parent="ScrollContainer/TimelineContainer/PanelContainer/HBoxContainer/TimelineScroll/LayersAndFrames/FrameButtonsAndIds"]
margin_right = 36.0
margin_bottom = 20.0
margin_bottom = 16.0
rect_min_size = Vector2( 0, 16 )
[node name="FrameButton" parent="ScrollContainer/TimelineContainer/PanelContainer/HBoxContainer/TimelineScroll/LayersAndFrames/FrameButtonsAndIds/FrameIDs" instance=ExtResource( 9 )]
margin_right = 36.0
rect_min_size = Vector2( 36, 0 )
[node name="FramesContainer" type="VBoxContainer" parent="ScrollContainer/TimelineContainer/PanelContainer/HBoxContainer/TimelineScroll/LayersAndFrames/FrameButtonsAndIds"]
margin_top = 24.0
margin_right = 36.0
margin_bottom = 24.0
margin_top = 20.0
margin_bottom = 20.0
[node name="EndSpacer" type="Control" parent="ScrollContainer/TimelineContainer/PanelContainer/HBoxContainer"]
margin_left = 888.0
@ -994,13 +1016,21 @@ autowrap = true
[node name="FrameTagDialog" parent="." instance=ExtResource( 42 )]
[node name="DragHighlight" type="ColorRect" parent="."]
visible = false
margin_right = 40.0
margin_bottom = 40.0
mouse_filter = 2
color = Color( 0, 0.741176, 1, 0.501961 )
[connection signal="item_rect_changed" from="." to="." method="_on_AnimationTimeline_item_rect_changed"]
[connection signal="item_rect_changed" from="ScrollContainer/TimelineContainer" to="." method="_on_TimelineContainer_item_rect_changed"]
[connection signal="pressed" from="ScrollContainer/TimelineContainer/TimelineButtons/LayerButtonPanelContainer/LayerButtons/AddLayer" to="." method="add_layer" binds= [ true ]]
[connection signal="pressed" from="ScrollContainer/TimelineContainer/TimelineButtons/LayerButtonPanelContainer/LayerButtons/AddLayer" to="." method="_on_AddLayer_pressed"]
[connection signal="pressed" from="ScrollContainer/TimelineContainer/TimelineButtons/LayerButtonPanelContainer/LayerButtons/AddGroup" to="." method="_on_AddGroup_pressed"]
[connection signal="pressed" from="ScrollContainer/TimelineContainer/TimelineButtons/LayerButtonPanelContainer/LayerButtons/RemoveLayer" to="." method="_on_RemoveLayer_pressed"]
[connection signal="pressed" from="ScrollContainer/TimelineContainer/TimelineButtons/LayerButtonPanelContainer/LayerButtons/MoveUpLayer" to="." method="change_layer_order" binds= [ 1 ]]
[connection signal="pressed" from="ScrollContainer/TimelineContainer/TimelineButtons/LayerButtonPanelContainer/LayerButtons/MoveDownLayer" to="." method="change_layer_order" binds= [ -1 ]]
[connection signal="pressed" from="ScrollContainer/TimelineContainer/TimelineButtons/LayerButtonPanelContainer/LayerButtons/CloneLayer" to="." method="add_layer" binds= [ false ]]
[connection signal="pressed" from="ScrollContainer/TimelineContainer/TimelineButtons/LayerButtonPanelContainer/LayerButtons/MoveUpLayer" to="." method="change_layer_order" binds= [ true ]]
[connection signal="pressed" from="ScrollContainer/TimelineContainer/TimelineButtons/LayerButtonPanelContainer/LayerButtons/MoveDownLayer" to="." method="change_layer_order" binds= [ false ]]
[connection signal="pressed" from="ScrollContainer/TimelineContainer/TimelineButtons/LayerButtonPanelContainer/LayerButtons/CloneLayer" to="." method="_on_CloneLayer_pressed"]
[connection signal="pressed" from="ScrollContainer/TimelineContainer/TimelineButtons/LayerButtonPanelContainer/LayerButtons/MergeDownLayer" to="." method="_on_MergeDownLayer_pressed"]
[connection signal="pressed" from="ScrollContainer/TimelineContainer/TimelineButtons/PanelContainer/AnimationButtons/FrameButtons/AddFrame" to="." method="add_frame"]
[connection signal="pressed" from="ScrollContainer/TimelineContainer/TimelineButtons/PanelContainer/AnimationButtons/FrameButtons/DeleteFrame" to="." method="_on_DeleteFrame_pressed"]

View file

@ -1,11 +1,10 @@
[gd_scene load_steps=5 format=2]
[gd_scene load_steps=4 format=2]
[ext_resource path="res://src/UI/Timeline/CelButton.gd" type="Script" id=1]
[ext_resource path="res://src/Shaders/TransparentChecker.shader" type="Shader" id=1]
[ext_resource path="res://src/UI/TransparentChecker.tscn" type="PackedScene" id=2]
[ext_resource path="res://src/Shaders/TransparentChecker.shader" type="Shader" id=3]
[sub_resource type="ShaderMaterial" id=1]
shader = ExtResource( 3 )
shader = ExtResource( 1 )
shader_param/size = 10.0
shader_param/alpha = 1.0
shader_param/color1 = Color( 0.7, 0.7, 0.7, 1 )
@ -16,17 +15,18 @@ shader_param/rect_size = Vector2( 0, 0 )
shader_param/follow_movement = false
shader_param/follow_scale = false
[node name="CelButton" type="Button"]
[node name="BaseCelButton" type="Button"]
margin_top = 18.0
margin_right = 36.0
margin_bottom = 54.0
rect_min_size = Vector2( 36, 36 )
focus_mode = 0
mouse_default_cursor_shape = 2
size_flags_horizontal = 0
size_flags_vertical = 0
toggle_mode = true
button_mask = 7
script = ExtResource( 1 )
enabled_focus_mode = 0
__meta__ = {
"_edit_use_anchors_": false
}
@ -53,21 +53,5 @@ anchor_bottom = 1.0
margin_right = 0.0
margin_bottom = 0.0
[node name="PopupMenu" type="PopupMenu" parent="."]
margin_right = 20.0
margin_bottom = 20.0
mouse_default_cursor_shape = 2
items = [ "Delete", null, 0, false, false, -1, 0, null, "", false, "Link Cel", null, 0, false, false, -1, 0, null, "", false ]
__meta__ = {
"_edit_use_anchors_": false
}
[node name="LinkedIndicator" type="Polygon2D" parent="."]
color = Color( 0.0627451, 0.741176, 0.215686, 1 )
invert_enable = true
invert_border = 1.0
polygon = PoolVector2Array( 0, 0, 36, 0, 36, 36, 0, 36 )
[connection signal="pressed" from="." to="." method="_on_CelButton_pressed"]
[connection signal="resized" from="." to="." method="_on_CelButton_resized"]
[connection signal="id_pressed" from="PopupMenu" to="." method="_on_PopupMenu_id_pressed"]

View file

@ -1,18 +1,19 @@
[gd_scene load_steps=5 format=2]
[gd_scene load_steps=4 format=2]
[ext_resource path="res://src/UI/Timeline/LayerButton.gd" type="Script" id=1]
[ext_resource path="res://assets/graphics/layers/layer_visible.png" type="Texture" id=2]
[ext_resource path="res://assets/graphics/layers/unlock.png" type="Texture" id=3]
[ext_resource path="res://assets/graphics/layers/unlinked_layer.png" type="Texture" id=4]
[node name="LayerContainer" type="Button"]
margin_right = 210.0
[node name="BaseLayerButton" type="Button"]
margin_right = 236.0
margin_bottom = 36.0
rect_min_size = Vector2( 212, 36 )
rect_min_size = Vector2( 236, 36 )
focus_mode = 0
mouse_default_cursor_shape = 2
size_flags_horizontal = 0
toggle_mode = true
action_mode = 0
enabled_focus_mode = 0
script = ExtResource( 1 )
__meta__ = {
"_edit_horizontal_guides_": [ ],
@ -29,6 +30,7 @@ __meta__ = {
[node name="EmptySpacer" type="Control" parent="HBoxContainer"]
margin_bottom = 36.0
mouse_filter = 2
[node name="LayerButtons" type="HBoxContainer" parent="HBoxContainer"]
margin_left = 4.0
@ -36,11 +38,37 @@ margin_right = 90.0
margin_bottom = 36.0
custom_constants/separation = 10
[node name="VisibilityButton" type="Button" parent="HBoxContainer/LayerButtons" groups=["UIButtons"]]
[node name="ExpandButton" type="ToolButton" parent="HBoxContainer/LayerButtons" groups=["UIButtons"]]
margin_top = 7.0
margin_right = 22.0
margin_bottom = 29.0
rect_min_size = Vector2( 22, 22 )
hint_tooltip = "Expand/collapse group"
mouse_default_cursor_shape = 2
size_flags_horizontal = 0
size_flags_vertical = 4
[node name="TextureRect" type="TextureRect" parent="HBoxContainer/LayerButtons/ExpandButton"]
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
margin_left = -11.0
margin_top = -11.0
margin_right = 11.0
margin_bottom = 11.0
size_flags_horizontal = 0
size_flags_vertical = 0
__meta__ = {
"_edit_use_anchors_": false
}
[node name="VisibilityButton" type="ToolButton" parent="HBoxContainer/LayerButtons" groups=["UIButtons"]]
margin_left = 32.0
margin_top = 7.0
margin_right = 54.0
margin_bottom = 29.0
rect_min_size = Vector2( 22, 22 )
hint_tooltip = "Toggle layer's visibility"
focus_mode = 0
mouse_default_cursor_shape = 2
@ -63,10 +91,10 @@ __meta__ = {
"_edit_use_anchors_": false
}
[node name="LockButton" type="Button" parent="HBoxContainer/LayerButtons" groups=["UIButtons"]]
margin_left = 32.0
[node name="LockButton" type="ToolButton" parent="HBoxContainer/LayerButtons" groups=["UIButtons"]]
margin_left = 64.0
margin_top = 7.0
margin_right = 54.0
margin_right = 86.0
margin_bottom = 29.0
rect_min_size = Vector2( 22, 22 )
hint_tooltip = "Lock/unlock layer"
@ -91,72 +119,53 @@ __meta__ = {
"_edit_use_anchors_": false
}
[node name="LinkButton" type="Button" parent="HBoxContainer/LayerButtons" groups=["UIButtons"]]
margin_left = 64.0
margin_top = 7.0
margin_right = 86.0
margin_bottom = 29.0
rect_min_size = Vector2( 22, 22 )
hint_tooltip = "Enable/disable cel linking
Linked cels are being shared across multiple frames"
focus_mode = 0
mouse_default_cursor_shape = 2
size_flags_horizontal = 0
size_flags_vertical = 4
[node name="TextureRect" type="TextureRect" parent="HBoxContainer/LayerButtons/LinkButton"]
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
margin_left = -11.0
margin_top = -11.0
margin_right = 11.0
margin_bottom = 11.0
size_flags_horizontal = 0
size_flags_vertical = 0
texture = ExtResource( 4 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="LayerName" type="HBoxContainer" parent="HBoxContainer"]
margin_left = 94.0
margin_right = 198.0
margin_left = 126.0
margin_right = 236.0
margin_bottom = 36.0
rect_min_size = Vector2( 104, 0 )
rect_min_size = Vector2( 110, 0 )
rect_pivot_offset = Vector2( -187, -9 )
mouse_default_cursor_shape = 2
size_flags_horizontal = 0
size_flags_horizontal = 10
alignment = 1
__meta__ = {
"_edit_use_anchors_": false
}
[node name="HierarchySpacer" type="Control" parent="HBoxContainer/LayerName"]
margin_bottom = 36.0
mouse_filter = 2
[node name="Label" type="Label" parent="HBoxContainer/LayerName"]
margin_left = 4.0
margin_top = 11.0
margin_right = 104.0
margin_right = 106.0
margin_bottom = 25.0
size_flags_horizontal = 3
text = "Layer 0"
align = 1
clip_text = true
[node name="LineEdit" type="LineEdit" parent="HBoxContainer/LayerName"]
visible = false
margin_left = 86.0
margin_top = 5.0
margin_right = 166.0
margin_bottom = 37.0
rect_min_size = Vector2( 80, 32 )
margin_left = 30.0
margin_top = 2.0
margin_right = 110.0
margin_bottom = 34.0
size_flags_horizontal = 3
size_flags_vertical = 4
text = "Layer 0"
editable = false
caret_blink = true
caret_blink_speed = 0.5
[node name="EmptySpacer" type="Control" parent="HBoxContainer/LayerName"]
margin_left = 110.0
margin_right = 110.0
margin_bottom = 36.0
mouse_filter = 2
[connection signal="gui_input" from="." to="." method="_on_LayerContainer_gui_input"]
[connection signal="pressed" from="HBoxContainer/LayerButtons/ExpandButton" to="." method="_on_ExpandButton_pressed"]
[connection signal="pressed" from="HBoxContainer/LayerButtons/VisibilityButton" to="." method="_on_VisibilityButton_pressed"]
[connection signal="pressed" from="HBoxContainer/LayerButtons/LockButton" to="." method="_on_LockButton_pressed"]
[connection signal="pressed" from="HBoxContainer/LayerButtons/LinkButton" to="." method="_on_LinkButton_pressed"]
[connection signal="focus_exited" from="HBoxContainer/LayerName/LineEdit" to="." method="_on_LineEdit_focus_exited"]

View file

@ -4,10 +4,10 @@ enum MenuOptions { DELETE, LINK, PROPERTIES }
var frame := 0
var layer := 0
var cel: Cel
var image: Image
var cel: BaseCel
onready var popup_menu: PopupMenu = $PopupMenu
onready var popup_menu: PopupMenu = get_node_or_null("PopupMenu")
onready var linked_indicator: Polygon2D = get_node_or_null("LinkedIndicator")
func _ready() -> void:
@ -19,30 +19,31 @@ func button_setup() -> void:
rect_min_size.y = Global.animation_timeline.cel_size
hint_tooltip = tr("Frame: %s, Layer: %s") % [frame + 1, layer]
if Global.current_project.frames[frame] in Global.current_project.layers[layer].linked_cels:
get_node("LinkedIndicator").visible = true
popup_menu.set_item_text(MenuOptions.LINK, "Unlink Cel")
popup_menu.set_item_metadata(MenuOptions.LINK, "Unlink Cel")
else:
get_node("LinkedIndicator").visible = false
popup_menu.set_item_text(MenuOptions.LINK, "Link Cel")
popup_menu.set_item_metadata(MenuOptions.LINK, "Link Cel")
if is_instance_valid(linked_indicator):
if Global.current_project.frames[frame] in Global.current_project.layers[layer].linked_cels:
linked_indicator.visible = true
popup_menu.set_item_text(MenuOptions.LINK, "Unlink Cel")
popup_menu.set_item_metadata(MenuOptions.LINK, "Unlink Cel")
else:
linked_indicator.visible = false
popup_menu.set_item_text(MenuOptions.LINK, "Link Cel")
popup_menu.set_item_metadata(MenuOptions.LINK, "Link Cel")
# Reset the checkers size because it assumes you want the same size as the canvas
var checker = $CelTexture/TransparentChecker
checker.rect_size = checker.get_parent().rect_size
cel = Global.current_project.frames[frame].cels[layer]
image = cel.image
func _on_CelButton_resized() -> void:
get_node("CelTexture").rect_min_size.x = rect_min_size.x - 4
get_node("CelTexture").rect_min_size.y = rect_min_size.y - 4
get_node("LinkedIndicator").polygon[1].x = rect_min_size.x
get_node("LinkedIndicator").polygon[2].x = rect_min_size.x
get_node("LinkedIndicator").polygon[2].y = rect_min_size.y
get_node("LinkedIndicator").polygon[3].y = rect_min_size.y
if is_instance_valid(linked_indicator):
linked_indicator.polygon[1].x = rect_min_size.x
linked_indicator.polygon[2].x = rect_min_size.x
linked_indicator.polygon[2].y = rect_min_size.y
linked_indicator.polygon[3].y = rect_min_size.y
func _on_CelButton_pressed() -> void:
@ -88,7 +89,8 @@ func _on_CelButton_pressed() -> void:
release_focus()
elif Input.is_action_just_released("right_mouse"):
popup_menu.popup(Rect2(get_global_mouse_position(), Vector2.ONE))
if is_instance_valid(popup_menu):
popup_menu.popup(Rect2(get_global_mouse_position(), Vector2.ONE))
pressed = !pressed
elif Input.is_action_just_released("middle_mouse"):
pressed = !pressed
@ -103,76 +105,71 @@ func _on_PopupMenu_id_pressed(id: int) -> void:
_delete_cel_content()
MenuOptions.LINK:
var f: Frame = Global.current_project.frames[frame]
var cel_index: int = Global.current_project.layers[layer].linked_cels.find(f)
var new_layers: Array = Global.current_project.duplicate_layers()
var new_cels: Array = f.cels.duplicate()
for i in new_cels.size():
new_cels[i] = Cel.new(
new_cels[i].image, new_cels[i].opacity, new_cels[i].image_texture
)
var project: Project = Global.current_project
var f: Frame = project.frames[frame]
var cel_index: int = project.layers[layer].linked_cels.find(f)
var new_linked_cels: Array = project.layers[layer].linked_cels.duplicate()
if popup_menu.get_item_metadata(MenuOptions.LINK) == "Unlink Cel":
new_layers[layer].linked_cels.remove(cel_index)
var sprite := Image.new()
sprite.copy_from(f.cels[layer].image)
var sprite_texture := ImageTexture.new()
sprite_texture.create_from_image(sprite, 0)
new_cels[layer].image = sprite
new_cels[layer].image_texture = sprite_texture
Global.current_project.undo_redo.create_action("Unlink Cel")
Global.current_project.undo_redo.add_do_property(
Global.current_project, "layers", new_layers
)
Global.current_project.undo_redo.add_do_property(f, "cels", new_cels)
Global.current_project.undo_redo.add_undo_property(
Global.current_project, "layers", Global.current_project.layers
)
Global.current_project.undo_redo.add_undo_property(f, "cels", f.cels)
Global.current_project.undo_redo.add_undo_method(Global, "undo_or_redo", true)
Global.current_project.undo_redo.add_do_method(Global, "undo_or_redo", false)
Global.current_project.undo_redo.commit_action()
new_linked_cels.remove(cel_index)
project.undo_redo.create_action("Unlink Cel")
project.undo_redo.add_do_property(cel, "image_texture", ImageTexture.new())
project.undo_redo.add_undo_property(cel, "image_texture", cel.image_texture)
project.undo_redo.add_do_method(cel, "set_content", cel.copy_content())
project.undo_redo.add_undo_method(cel, "set_content", cel.get_content())
elif popup_menu.get_item_metadata(MenuOptions.LINK) == "Link Cel":
new_layers[layer].linked_cels.append(f)
Global.current_project.undo_redo.create_action("Link Cel")
Global.current_project.undo_redo.add_do_property(
Global.current_project, "layers", new_layers
)
if new_layers[layer].linked_cels.size() > 1:
new_linked_cels.append(f)
project.undo_redo.create_action("Link Cel")
if new_linked_cels.size() > 1:
# If there are already linked cels, set the current cel's image
# to the first linked cel's image
new_cels[layer].image = new_layers[layer].linked_cels[0].cels[layer].image
new_cels[layer].image_texture = new_layers[layer].linked_cels[0].cels[layer].image_texture
Global.current_project.undo_redo.add_do_property(f, "cels", new_cels)
Global.current_project.undo_redo.add_undo_property(f, "cels", f.cels)
var linked_cel: BaseCel = project.layers[layer].linked_cels[0].cels[layer]
project.undo_redo.add_do_property(
cel, "image_texture", linked_cel.image_texture
)
project.undo_redo.add_undo_property(cel, "image_texture", cel.image_texture)
project.undo_redo.add_do_method(cel, "set_content", linked_cel.get_content())
project.undo_redo.add_undo_method(cel, "set_content", cel.get_content())
Global.current_project.undo_redo.add_undo_property(
Global.current_project, "layers", Global.current_project.layers
)
Global.current_project.undo_redo.add_undo_method(Global, "undo_or_redo", true)
Global.current_project.undo_redo.add_do_method(Global, "undo_or_redo", false)
Global.current_project.undo_redo.commit_action()
project.undo_redo.add_do_property(project.layers[layer], "linked_cels", new_linked_cels)
project.undo_redo.add_undo_property(
project.layers[layer], "linked_cels", project.layers[layer].linked_cels
)
# Remove and add a new cel button to update appearance (can't use self.button_setup
# because there is no guarantee that it will be the exact same cel button instance)
project.undo_redo.add_do_method(
Global.animation_timeline, "project_cel_removed", frame, layer
)
project.undo_redo.add_undo_method(
Global.animation_timeline, "project_cel_removed", frame, layer
)
project.undo_redo.add_do_method(
Global.animation_timeline, "project_cel_added", frame, layer
)
project.undo_redo.add_undo_method(
Global.animation_timeline, "project_cel_added", frame, layer
)
project.undo_redo.add_do_method(Global, "undo_or_redo", false)
project.undo_redo.add_undo_method(Global, "undo_or_redo", true)
project.undo_redo.commit_action()
func _delete_cel_content() -> void:
if image.is_invisible():
return
var curr_layer: Layer = Global.current_project.layers[layer]
if !curr_layer.can_layer_get_drawn():
return
var project = Global.current_project
image.unlock()
var data := image.data
var empty_content = cel.create_empty_content()
var old_content = cel.get_content()
project.undos += 1
project.undo_redo.create_action("Draw")
project.undo_redo.add_undo_property(image, "data", data)
project.undo_redo.add_undo_method(Global, "undo_or_redo", true, frame, layer, project)
image.fill(0)
project.undo_redo.add_do_property(image, "data", image.data)
if project.frames[frame] in project.layers[layer].linked_cels:
for f in project.layers[layer].linked_cels:
project.undo_redo.add_do_method(f.cels[layer], "set_content", empty_content)
project.undo_redo.add_undo_method(f.cels[layer], "set_content", old_content)
else:
project.undo_redo.add_do_method(cel, "set_content", empty_content)
project.undo_redo.add_undo_method(cel, "set_content", old_content)
project.undo_redo.add_do_method(Global, "undo_or_redo", false, frame, layer, project)
project.undo_redo.add_undo_method(Global, "undo_or_redo", true, frame, layer, project)
project.undo_redo.commit_action()
@ -192,72 +189,70 @@ func get_drag_data(_position) -> Array:
func can_drop_data(_pos, data) -> bool:
var project: Project = Global.current_project
if typeof(data) == TYPE_ARRAY and data[0] == "Cel":
var new_frame = data[1]
var new_layer = data[2]
if (
Global.current_project.frames[frame] in Global.current_project.layers[layer].linked_cels
or (
Global.current_project.frames[new_frame]
in Global.current_project.layers[new_layer].linked_cels
)
):
# If the cel we're dragging or the cel we are targeting are linked, don't allow dragging
return false
else:
return true
else:
return false
var drag_frame = data[1]
var drag_layer = data[2]
if project.layers[drag_layer].get_script() == project.layers[layer].get_script():
if (
project.layers[layer] is GroupLayer
or not (
(project.frames[frame] in project.layers[layer].linked_cels)
or (project.frames[drag_frame] in project.layers[drag_layer].linked_cels)
)
):
if not (drag_frame == frame and drag_layer == layer):
var region: Rect2
if Input.is_action_pressed("ctrl") or layer != drag_layer: # Swap cels
region = get_global_rect()
else: # Move cels
if _get_region_rect(0, 0.5).has_point(get_global_mouse_position()): # Left
region = _get_region_rect(-0.125, 0.125)
region.position.x -= 2 # Container spacing
else: # Right
region = _get_region_rect(0.875, 1.125)
region.position.x += 2 # Container spacing
Global.animation_timeline.drag_highlight.rect_global_position = region.position
Global.animation_timeline.drag_highlight.rect_size = region.size
Global.animation_timeline.drag_highlight.visible = true
return true
Global.animation_timeline.drag_highlight.visible = false
return false
func drop_data(_pos, data) -> void:
var new_frame = data[1]
var new_layer = data[2]
if new_frame == frame and new_layer == layer:
return
var drop_frame = data[1]
var drop_layer = data[2]
var project = Global.current_project
var this_frame_new_cels = Global.current_project.frames[frame].cels.duplicate()
var new_frame_new_cels
var temp = this_frame_new_cels[layer]
this_frame_new_cels[layer] = Global.current_project.frames[new_frame].cels[new_layer]
if frame == new_frame:
this_frame_new_cels[new_layer] = temp
else:
new_frame_new_cels = Global.current_project.frames[new_frame].cels.duplicate()
new_frame_new_cels[new_layer] = temp
project.undo_redo.create_action("Move Cels")
if Input.is_action_pressed("ctrl") or layer != drop_layer: # Swap cels
project.undo_redo.add_do_method(project, "swap_cel", frame, layer, drop_frame, drop_layer)
project.undo_redo.add_undo_method(project, "swap_cel", frame, layer, drop_frame, drop_layer)
else: # Move cels
var to_frame: int
if _get_region_rect(0, 0.5).has_point(get_global_mouse_position()): # Left
to_frame = frame
else: # Right
to_frame = frame + 1
if drop_frame < frame:
to_frame -= 1
project.undo_redo.add_do_method(project, "move_cel", drop_frame, to_frame, layer)
project.undo_redo.add_undo_method(project, "move_cel", to_frame, drop_frame, layer)
Global.current_project.undo_redo.create_action("Move Cels")
Global.current_project.undo_redo.add_do_property(
Global.current_project.frames[frame], "cels", this_frame_new_cels
)
project.undo_redo.add_do_property(project, "current_layer", layer)
project.undo_redo.add_undo_property(project, "current_layer", project.current_layer)
if frame != drop_frame: # If the cel moved to a different frame
project.undo_redo.add_do_property(project, "current_frame", frame)
project.undo_redo.add_undo_property(project, "current_frame", project.current_frame)
project.undo_redo.add_undo_method(Global, "undo_or_redo", true)
project.undo_redo.add_do_method(Global, "undo_or_redo", false)
project.undo_redo.commit_action()
Global.current_project.undo_redo.add_do_property(Global.current_project, "current_layer", layer)
Global.current_project.undo_redo.add_undo_property(
Global.current_project, "current_layer", Global.current_project.current_layer
)
if frame != new_frame: # If the cel moved to a different frame
Global.current_project.undo_redo.add_do_property(
Global.current_project.frames[new_frame], "cels", new_frame_new_cels
)
Global.current_project.undo_redo.add_do_property(
Global.current_project, "current_frame", frame
)
Global.current_project.undo_redo.add_undo_property(
Global.current_project, "current_frame", Global.current_project.current_frame
)
Global.current_project.undo_redo.add_undo_property(
Global.current_project.frames[new_frame],
"cels",
Global.current_project.frames[new_frame].cels
)
Global.current_project.undo_redo.add_undo_property(
Global.current_project.frames[frame], "cels", Global.current_project.frames[frame].cels
)
Global.current_project.undo_redo.add_undo_method(Global, "undo_or_redo", true)
Global.current_project.undo_redo.add_do_method(Global, "undo_or_redo", false)
Global.current_project.undo_redo.commit_action()
func _get_region_rect(x_begin: float, x_end: float) -> Rect2:
var rect := get_global_rect()
rect.position.x += rect.size.x * x_begin
rect.size.x *= x_end - x_begin
return rect

View file

@ -7,6 +7,8 @@ onready var frame_properties: ConfirmationDialog = Global.control.find_node("Fra
func _ready() -> void:
rect_min_size.x = Global.animation_timeline.cel_size
text = str(frame + 1)
connect("pressed", self, "_button_pressed")
connect("mouse_entered", self, "_update_tooltip")
@ -85,33 +87,21 @@ func _on_PopupMenu_id_pressed(id: int) -> void:
func change_frame_order(rate: int) -> void:
var change = frame + rate
var new_frames: Array = Global.current_project.frames.duplicate()
var temp = new_frames[frame]
new_frames[frame] = new_frames[change]
new_frames[change] = temp
var project = Global.current_project
Global.current_project.undo_redo.create_action("Change Frame Order")
Global.current_project.undo_redo.add_do_property(Global.current_project, "frames", new_frames)
Global.current_project.undo_redo.add_undo_property(
Global.current_project, "frames", Global.current_project.frames
)
project.undo_redo.create_action("Change Frame Order")
project.undo_redo.add_do_method(project, "move_frame", frame, change)
project.undo_redo.add_undo_method(project, "move_frame", change, frame)
if Global.current_project.current_frame == frame:
Global.current_project.undo_redo.add_do_property(
Global.current_project, "current_frame", change
)
if project.current_frame == frame:
project.undo_redo.add_do_property(project, "current_frame", change)
else:
Global.current_project.undo_redo.add_do_property(
Global.current_project, "current_frame", Global.current_project.current_frame
)
project.undo_redo.add_do_property(project, "current_frame", project.current_frame)
Global.current_project.undo_redo.add_undo_property(
Global.current_project, "current_frame", Global.current_project.current_frame
)
Global.current_project.undo_redo.add_undo_method(Global, "undo_or_redo", true)
Global.current_project.undo_redo.add_do_method(Global, "undo_or_redo", false)
Global.current_project.undo_redo.commit_action()
project.undo_redo.add_undo_property(project, "current_frame", project.current_frame)
project.undo_redo.add_undo_method(Global, "undo_or_redo", true)
project.undo_redo.add_do_method(Global, "undo_or_redo", false)
project.undo_redo.commit_action()
func get_drag_data(_position) -> Array:
@ -126,40 +116,56 @@ func get_drag_data(_position) -> Array:
func can_drop_data(_pos, data) -> bool:
if typeof(data) == TYPE_ARRAY:
return data[0] == "Frame"
else:
return false
if data[0] == "Frame":
if data[1] != frame: # Can't move to same frame
var region: Rect2
if Input.is_action_pressed("ctrl"): # Swap frames
region = get_global_rect()
else: # Move frames
if _get_region_rect(0, 0.5).has_point(get_global_mouse_position()):
region = _get_region_rect(-0.125, 0.125)
region.position.x -= 2 # Container spacing
else:
region = _get_region_rect(0.875, 1.125)
region.position.x += 2 # Container spacing
Global.animation_timeline.drag_highlight.rect_global_position = region.position
Global.animation_timeline.drag_highlight.rect_size = region.size
Global.animation_timeline.drag_highlight.visible = true
return true
Global.animation_timeline.drag_highlight.visible = false
return false
func drop_data(_pos, data) -> void:
var new_frame = data[1]
if frame == new_frame:
return
var drop_frame = data[1]
var project = Global.current_project
project.undo_redo.create_action("Change Frame Order")
if Input.is_action_pressed("ctrl"): # Swap frames
project.undo_redo.add_do_method(project, "swap_frame", frame, drop_frame)
project.undo_redo.add_undo_method(project, "swap_frame", frame, drop_frame)
else: # Move frames
var to_frame: int
if _get_region_rect(0, 0.5).has_point(get_global_mouse_position()): # Left
to_frame = frame
else: # Right
to_frame = frame + 1
if drop_frame < frame:
to_frame -= 1
project.undo_redo.add_do_method(project, "move_frame", drop_frame, to_frame)
project.undo_redo.add_undo_method(project, "move_frame", to_frame, drop_frame)
var new_frames: Array = Global.current_project.frames.duplicate()
var temp = new_frames[frame]
new_frames[frame] = new_frames[new_frame]
new_frames[new_frame] = temp
Global.current_project.undo_redo.create_action("Change Frame Order")
Global.current_project.undo_redo.add_do_property(Global.current_project, "frames", new_frames)
Global.current_project.undo_redo.add_undo_property(
Global.current_project, "frames", Global.current_project.frames
)
if Global.current_project.current_frame == new_frame:
Global.current_project.undo_redo.add_do_property(
Global.current_project, "current_frame", frame
)
if project.current_frame == drop_frame:
project.undo_redo.add_do_property(project, "current_frame", frame)
else:
Global.current_project.undo_redo.add_do_property(
Global.current_project, "current_frame", Global.current_project.current_frame
)
project.undo_redo.add_do_property(project, "current_frame", project.current_frame)
project.undo_redo.add_undo_property(project, "current_frame", project.current_frame)
project.undo_redo.add_undo_method(Global, "undo_or_redo", true)
project.undo_redo.add_do_method(Global, "undo_or_redo", false)
project.undo_redo.commit_action()
Global.current_project.undo_redo.add_undo_property(
Global.current_project, "current_frame", Global.current_project.current_frame
)
Global.current_project.undo_redo.add_undo_method(Global, "undo_or_redo", true)
Global.current_project.undo_redo.add_do_method(Global, "undo_or_redo", false)
Global.current_project.undo_redo.commit_action()
func _get_region_rect(x_begin: float, x_end: float) -> Rect2:
var rect := get_global_rect()
rect.position.x += rect.size.x * x_begin
rect.size.x *= x_end - x_begin
return rect

View file

@ -5,9 +5,11 @@
[node name="FrameButton" type="Button"]
margin_right = 12.0
margin_bottom = 20.0
focus_mode = 0
mouse_default_cursor_shape = 2
toggle_mode = true
button_mask = 7
enabled_focus_mode = 0
text = "1"
script = ExtResource( 1 )
__meta__ = {

View file

@ -0,0 +1,10 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://src/UI/Timeline/BaseCelButton.tscn" type="PackedScene" id=1]
[ext_resource path="res://src/UI/Timeline/CelButton.gd" type="Script" id=2]
[node name="GroupCelButton" instance=ExtResource( 1 )]
script = ExtResource( 2 )
[node name="TransparentChecker" parent="CelTexture" index="0"]
visible = false

View file

@ -0,0 +1,10 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://src/UI/Timeline/BaseLayerButton.tscn" type="PackedScene" id=1]
[ext_resource path="res://assets/graphics/layers/group_expanded.png" type="Texture" id=4]
[node name="GroupLayerButton" instance=ExtResource( 1 )]
hide_expand_button = false
[node name="TextureRect" parent="HBoxContainer/LayerButtons/ExpandButton" index="0"]
texture = ExtResource( 4 )

View file

@ -1,28 +1,54 @@
class_name LayerButton
extends Button
const HIERARCHY_DEPTH_PIXEL_SHIFT = 8
export var hide_expand_button := true
var layer := 0
onready var expand_button: BaseButton = find_node("ExpandButton")
onready var visibility_button: BaseButton = find_node("VisibilityButton")
onready var lock_button: BaseButton = find_node("LockButton")
onready var linked_button: BaseButton = find_node("LinkButton")
onready var label: Label = find_node("Label")
onready var line_edit: LineEdit = find_node("LineEdit")
onready var hierarchy_spacer: Control = find_node("HierarchySpacer")
onready var linked_button: BaseButton = find_node("LinkButton")
func _ready() -> void:
rect_min_size.y = Global.animation_timeline.cel_size
label.text = Global.current_project.layers[layer].name
line_edit.text = Global.current_project.layers[layer].name
var layer_buttons = find_node("LayerButtons")
for child in layer_buttons.get_children():
var texture = child.get_child(0)
var last_backslash = texture.texture.resource_path.get_base_dir().find_last("/")
var button_category = texture.texture.resource_path.get_base_dir().right(last_backslash + 1)
var normal_file_name = texture.texture.resource_path.get_file()
texture.texture = load("res://assets/graphics/%s/%s" % [button_category, normal_file_name])
texture.modulate = Global.modulate_icon_color
# Visualize how deep into the hierarchy the layer is
var hierarchy_depth: int = Global.current_project.layers[layer].get_hierarchy_depth()
hierarchy_spacer.rect_min_size.x = hierarchy_depth * HIERARCHY_DEPTH_PIXEL_SHIFT
if Global.control.theme.get_color("font_color", "Button").v > 0.5: # Light text is dark theme
self_modulate.v = 1 + hierarchy_depth * 0.4
else: # Dark text should be light theme
self_modulate.v = 1 - hierarchy_depth * 0.075
update_buttons()
func update_buttons() -> void:
if hide_expand_button:
expand_button.mouse_filter = Control.MOUSE_FILTER_IGNORE
expand_button.get_child(0).visible = false # Hide the TextureRect
else:
if Global.current_project.layers[layer].expanded:
Global.change_button_texturerect(expand_button.get_child(0), "group_expanded.png")
else:
Global.change_button_texturerect(expand_button.get_child(0), "group_collapsed.png")
if Global.current_project.layers[layer].visible:
Global.change_button_texturerect(visibility_button.get_child(0), "layer_visible.png")
else:
@ -33,10 +59,40 @@ func _ready() -> void:
else:
Global.change_button_texturerect(lock_button.get_child(0), "unlock.png")
if Global.current_project.layers[layer].new_cels_linked: # If new layers will be linked
Global.change_button_texturerect(linked_button.get_child(0), "linked_layer.png")
else:
Global.change_button_texturerect(linked_button.get_child(0), "unlinked_layer.png")
if linked_button:
if Global.current_project.layers[layer].new_cels_linked: # If new layers will be linked
Global.change_button_texturerect(linked_button.get_child(0), "linked_layer.png")
else:
Global.change_button_texturerect(linked_button.get_child(0), "unlinked_layer.png")
visibility_button.modulate.a = 1
lock_button.modulate.a = 1
if is_instance_valid(Global.current_project.layers[layer].parent):
if not Global.current_project.layers[layer].parent.is_visible_in_hierarchy():
visibility_button.modulate.a = 0.33
if Global.current_project.layers[layer].parent.is_locked_in_hierarchy():
lock_button.modulate.a = 0.33
# Used when pressing a button on this changes the appearnce of other layers (ie: expand or visible)
func _update_buttons_all_layers() -> void:
for layer_button in Global.layers_container.get_children():
layer_button.update_buttons()
var expanded = Global.current_project.layers[layer_button.layer].is_expanded_in_hierarchy()
layer_button.visible = expanded
Global.frames_container.get_child(layer_button.get_index()).visible = expanded
func _draw() -> void:
if hierarchy_spacer.rect_size.x > 0.1:
var color := Color(1, 1, 1, 0.33)
color.v = round(Global.control.theme.get_color("font_color", "Button").v)
var x = (
hierarchy_spacer.rect_global_position.x
- rect_global_position.x
+ hierarchy_spacer.rect_size.x
)
draw_line(Vector2(x, 0), Vector2(x, rect_size.y), color)
func _input(event: InputEvent) -> void:
@ -89,37 +145,44 @@ func _save_layer_name(new_name: String) -> void:
line_edit.visible = false
line_edit.editable = false
label.text = new_name
Global.layers_changed_skip = true
Global.current_project.layers[layer].name = new_name
func _on_ExpandButton_pressed():
Global.current_project.layers[layer].expanded = !Global.current_project.layers[layer].expanded
_update_buttons_all_layers()
func _on_VisibilityButton_pressed() -> void:
Global.canvas.selection.transform_content_confirm()
Global.current_project.layers[layer].visible = !Global.current_project.layers[layer].visible
Global.canvas.update()
_select_current_layer()
_update_buttons_all_layers()
func _on_LockButton_pressed() -> void:
Global.canvas.selection.transform_content_confirm()
Global.current_project.layers[layer].locked = !Global.current_project.layers[layer].locked
_select_current_layer()
_update_buttons_all_layers()
func _on_LinkButton_pressed() -> void:
Global.canvas.selection.transform_content_confirm()
var layer_class: Layer = Global.current_project.layers[layer]
var layer_class: PixelLayer = Global.current_project.layers[layer]
layer_class.new_cels_linked = !layer_class.new_cels_linked
if layer_class.new_cels_linked && !layer_class.linked_cels:
# If button is pressed and there are no linked cels in the layer
layer_class.linked_cels.append(
Global.current_project.frames[Global.current_project.current_frame]
)
var container = Global.frames_container.get_child(Global.current_project.current_layer)
var container = Global.frames_container.get_child(
Global.frames_container.get_child_count() - 1 - layer
)
container.get_child(Global.current_project.current_frame).button_setup()
_select_current_layer()
Global.current_project.layers = Global.current_project.layers # Call the setter
update_buttons()
func _select_current_layer() -> void:
@ -132,53 +195,171 @@ func _select_current_layer() -> void:
func get_drag_data(_position) -> Array:
var button := Button.new()
button.rect_size = rect_size
button.theme = Global.control.theme
button.text = label.text
set_drag_preview(button)
var layers := range(
layer - Global.current_project.layers[layer].get_child_count(true), layer + 1
)
var box := VBoxContainer.new()
for i in layers.size():
var button := Button.new()
button.rect_min_size = rect_size
button.theme = Global.control.theme
button.text = Global.current_project.layers[layers[-1 - i]].name
box.add_child(button)
set_drag_preview(box)
return ["Layer", layer]
func can_drop_data(_pos, data) -> bool:
if typeof(data) == TYPE_ARRAY:
return data[0] == "Layer"
else:
return false
if data[0] == "Layer":
var curr_layer: BaseLayer = Global.current_project.layers[layer]
var drag_layer: BaseLayer = Global.current_project.layers[data[1]]
if curr_layer == drag_layer:
Global.animation_timeline.drag_highlight.visible = false
return false
var region: Rect2
var depth: int = Global.current_project.layers[layer].get_hierarchy_depth()
if Input.is_action_pressed("ctrl"): # Swap layers
if drag_layer.is_a_parent_of(curr_layer) or curr_layer.is_a_parent_of(drag_layer):
Global.animation_timeline.drag_highlight.visible = false
return false
region = get_global_rect()
else: # Shift layers
if drag_layer.is_a_parent_of(curr_layer):
Global.animation_timeline.drag_highlight.visible = false
return false
# If accepted as a child, is it in the center region?
if (
Global.current_project.layers[layer].accepts_child(data[1])
and _get_region_rect(0.25, 0.75).has_point(get_global_mouse_position())
):
# Drawn regions are adusted a bit from actual to clearify drop position
region = _get_region_rect(0.15, 0.85)
depth += 1
else:
# Top or bottom region?
if _get_region_rect(0, 0.5).has_point(get_global_mouse_position()):
region = _get_region_rect(-0.1, 0.15)
else:
region = _get_region_rect(0.85, 1.1)
# Shift drawn region to the right a bit for hierarchy depth visualization:
region.position.x += depth * HIERARCHY_DEPTH_PIXEL_SHIFT
region.size.x -= depth * HIERARCHY_DEPTH_PIXEL_SHIFT
Global.animation_timeline.drag_highlight.rect_global_position = region.position
Global.animation_timeline.drag_highlight.rect_size = region.size
Global.animation_timeline.drag_highlight.visible = true
return true
Global.animation_timeline.drag_highlight.visible = false
return false
func drop_data(_pos, data) -> void:
var new_layer = data[1]
if layer == new_layer:
return
var drop_layer: int = data[1]
var project = Global.current_project
var new_layers: Array = Global.current_project.layers.duplicate()
var temp = new_layers[layer]
new_layers[layer] = new_layers[new_layer]
new_layers[new_layer] = temp
project.undo_redo.create_action("Change Layer Order")
var layers: Array = project.layers # This shouldn't be modified directly
Global.current_project.undo_redo.create_action("Change Layer Order")
for f in Global.current_project.frames:
var new_cels: Array = f.cels.duplicate()
var temp_canvas = new_cels[layer]
new_cels[layer] = new_cels[new_layer]
new_cels[new_layer] = temp_canvas
Global.current_project.undo_redo.add_do_property(f, "cels", new_cels)
Global.current_project.undo_redo.add_undo_property(f, "cels", f.cels)
if Global.current_project.current_layer == layer:
Global.current_project.undo_redo.add_do_property(
Global.current_project, "current_layer", new_layer
)
Global.current_project.undo_redo.add_undo_property(
Global.current_project, "current_layer", Global.current_project.current_layer
)
Global.current_project.undo_redo.add_do_property(Global.current_project, "layers", new_layers)
Global.current_project.undo_redo.add_undo_property(
Global.current_project, "layers", Global.current_project.layers
var drop_from_indices := range(
drop_layer - layers[drop_layer].get_child_count(true), drop_layer + 1
)
Global.current_project.undo_redo.add_undo_method(Global, "undo_or_redo", true)
Global.current_project.undo_redo.add_do_method(Global, "undo_or_redo", false)
Global.current_project.undo_redo.commit_action()
var drop_from_parents := []
for i in range(drop_from_indices.size()):
drop_from_parents.append(layers[drop_from_indices[i]].parent)
if Input.is_action_pressed("ctrl"): # Swap layers
# a and b both need "from", "to", and "to_parents"
# a is this layer (and children), b is the dropped layers
var a := {"from": range(layer - layers[layer].get_child_count(true), layer + 1)}
var b := {"from": drop_from_indices}
if a.from[0] < b.from[0]:
a["to"] = range(b.from[-1] + 1 - a.from.size(), b.from[-1] + 1) # Size of a, start from end of b
b["to"] = range(a.from[0], a.from[0] + b.from.size()) # Size of b, start from beginning of a
else:
a["to"] = range(b.from[0], b.from[0] + a.from.size()) # Size of a, start from beginning of b
b["to"] = range(a.from[-1] + 1 - b.from.size(), a.from[-1] + 1) # Size of b, start from end of a
var a_from_parents := []
for l in a.from:
a_from_parents.append(layers[l].parent)
# to_parents starts as a dulpicate of from_parents, set the root layer's (with one layer or
# group with its children, this will always be the last layer [-1]) parent to the other
# root layer's parent
a["to_parents"] = a_from_parents.duplicate()
b["to_parents"] = drop_from_parents.duplicate()
a.to_parents[-1] = drop_from_parents[-1]
b.to_parents[-1] = a_from_parents[-1]
project.undo_redo.add_do_method(project, "swap_layers", a, b)
project.undo_redo.add_undo_method(
project,
"swap_layers",
{"from": a.to, "to": a.from, "to_parents": a_from_parents},
{"from": b.to, "to": drop_from_indices, "to_parents": drop_from_parents}
)
else: # Move layers
var to_index: int # the index where the LOWEST moved layer should end up
var to_parent: BaseLayer
# If accepted as a child, is it in the center region?
if (
layers[layer].accepts_child(data[1])
and _get_region_rect(0.25, 0.75).has_point(get_global_mouse_position())
):
to_index = layer
to_parent = layers[layer]
else:
# Top or bottom region?
if _get_region_rect(0, 0.5).has_point(get_global_mouse_position()):
to_index = layer + 1
to_parent = layers[layer].parent
else:
# Place under the layer, if it has children, place after its lowest child
if layers[layer].has_children():
to_index = layers[layer].get_children(true)[0].index
if layers[layer].is_a_parent_of(layers[drop_layer]):
to_index += drop_from_indices.size()
else:
to_index = layer
to_parent = layers[layer].parent
if drop_layer < layer:
to_index -= drop_from_indices.size()
var drop_to_indices := range(to_index, to_index + drop_from_indices.size())
var to_parents := drop_from_parents.duplicate()
to_parents[-1] = to_parent
project.undo_redo.add_do_method(
project, "move_layers", drop_from_indices, drop_to_indices, to_parents
)
project.undo_redo.add_undo_method(
project, "move_layers", drop_to_indices, drop_from_indices, drop_from_parents
)
if project.current_layer == drop_layer:
project.undo_redo.add_do_property(project, "current_layer", layer)
else:
project.undo_redo.add_do_property(project, "current_layer", project.current_layer)
project.undo_redo.add_undo_property(project, "current_layer", project.current_layer)
project.undo_redo.add_undo_method(Global, "undo_or_redo", true)
project.undo_redo.add_do_method(Global, "undo_or_redo", false)
project.undo_redo.commit_action()
func _get_region_rect(y_begin: float, y_end: float) -> Rect2:
var rect := get_global_rect()
rect.position.y += rect.size.y * y_begin
rect.size.y *= y_end - y_begin
return rect

View file

@ -0,0 +1,25 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://src/UI/Timeline/BaseCelButton.tscn" type="PackedScene" id=1]
[ext_resource path="res://src/UI/Timeline/CelButton.gd" type="Script" id=2]
[node name="PixelCelButton" instance=ExtResource( 1 )]
rect_pivot_offset = Vector2( -18, 6 )
script = ExtResource( 2 )
[node name="PopupMenu" type="PopupMenu" parent="." index="1"]
margin_right = 20.0
margin_bottom = 20.0
mouse_default_cursor_shape = 2
items = [ "Delete", null, 0, false, false, -1, 0, null, "", false, "Link Cel", null, 0, false, false, -1, 0, null, "", false ]
__meta__ = {
"_edit_use_anchors_": false
}
[node name="LinkedIndicator" type="Polygon2D" parent="." index="2"]
color = Color( 0.0627451, 0.741176, 0.215686, 1 )
invert_enable = true
invert_border = 1.0
polygon = PoolVector2Array( 0, 0, 36, 0, 36, 36, 0, 36 )
[connection signal="id_pressed" from="PopupMenu" to="." method="_on_PopupMenu_id_pressed"]

View file

@ -0,0 +1,40 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://src/UI/Timeline/BaseLayerButton.tscn" type="PackedScene" id=1]
[ext_resource path="res://assets/graphics/layers/unlinked_layer.png" type="Texture" id=4]
[node name="PixelLayerButton" instance=ExtResource( 1 )]
[node name="LayerButtons" parent="HBoxContainer" index="1"]
margin_right = 122.0
[node name="LinkButton" type="ToolButton" parent="HBoxContainer/LayerButtons" index="3" groups=["UIButtons"]]
margin_left = 96.0
margin_top = 7.0
margin_right = 118.0
margin_bottom = 29.0
rect_min_size = Vector2( 22, 22 )
hint_tooltip = "Enable/disable cel linking
Linked cels are being shared across multiple frames"
mouse_default_cursor_shape = 2
size_flags_horizontal = 0
size_flags_vertical = 4
[node name="TextureRect" type="TextureRect" parent="HBoxContainer/LayerButtons/LinkButton" index="0"]
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
margin_left = -11.0
margin_top = -11.0
margin_right = 11.0
margin_bottom = 11.0
size_flags_horizontal = 0
size_flags_vertical = 0
texture = ExtResource( 4 )
__meta__ = {
"_edit_use_anchors_": false
}
[connection signal="pressed" from="HBoxContainer/LayerButtons/LinkButton" to="." method="_on_LinkButton_pressed"]