Skip to content

Commit d5feeb2

Browse files
authored
feat: Timeline (#31)
* Timeline Finished the basic setup of the timeline, completed the following features: Display each frame in order Allow the user to make a selection of layers in the timeline The lower half of the timeline panel is leave for the audio timeline The features of the timeline are not fully completed yet; features under development: 1. copy layer 2. delete layer 3. add layer * Multi-layer Feature Now each page can have multiple layers * Pasteboard and deletion * Shortcuts * rename * Format * reorder claims * Update panel_controls.gd * Fixing _creating_new_layer setup the arguments correctly * Update timeline_manager.gd reformat * Initialize the timeline * Order the pasted layers The pasted layers will follow the original order.
1 parent 547db8e commit d5feeb2

18 files changed

Lines changed: 693 additions & 39 deletions

File tree

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"godotTools.editorPath.godot4": "/Applications/Godot.app"
3+
}

system/canvas/canvas.gd

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ func set_onion_skin_depth(new_depth: int) -> void:
6666
## Bakes [code]dynamic_node[/code] contents to the current page.
6767
func bake_page() -> void:
6868
# Getting items from our project.
69-
var current_page = _project.frames[_project._current_frame]
70-
var current_layer = _project._current_layer + 1
69+
var current_page = _project.frames[_project.current_frame]
70+
var current_layer = _project.current_layer
7171

7272
bake_viewport.size = Vector2(_project.width, _project.height)
7373
bake_viewport.render_target_clear_mode = SubViewport.CLEAR_MODE_ALWAYS

system/page/page.gd

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
class_name Page
22
extends Resource
33

4+
signal page_update
5+
46
@export var layers: Array[Image] = []
57
var textures: Array[ImageTexture] = []
8+
var names: Array[String] = []
69

710

811
func _init(w: int = 0, h: int = 0) -> void:
@@ -16,10 +19,15 @@ func _init(w: int = 0, h: int = 0) -> void:
1619
## [param w] - Width of layer. [br]
1720
## [param h] - Height of layer. [br]
1821
## [param c] - Color of background. Defaults to transparent.
19-
func create_layer(w: int, h: int, c: Color = Color.TRANSPARENT) -> void:
20-
layers.append(Image.create_empty(w, h, false, Image.FORMAT_RGBA8))
21-
layers[-1].fill(c)
22-
textures.append(ImageTexture.create_from_image(layers[-1]))
22+
func create_layer(w: int, h: int, c: Color = Color.TRANSPARENT, idx: int = -1) -> void:
23+
if idx == -1:
24+
idx = layers.size()
25+
layers.insert(idx, Image.create_empty(w, h, false, Image.FORMAT_RGBA8))
26+
layers[idx].fill(c)
27+
var new_name = "layer " + str(layers.size() - 1)
28+
names.insert(idx, new_name)
29+
textures.insert(idx, ImageTexture.create_from_image(layers[idx]))
30+
page_update.emit()
2331

2432

2533
## Returns the Page as an array of ImageTextures.
@@ -29,6 +37,7 @@ func get_content() -> Array[ImageTexture]:
2937
textures[i].update(layers[i])
3038
else:
3139
textures.append(ImageTexture.create_from_image(layers[i]))
40+
names.append("layer " + str(i))
3241
return textures
3342

3443

@@ -37,6 +46,7 @@ func set_content() -> void:
3746
for i in range(layers.size()):
3847
if i < textures.size():
3948
layers[i] = textures[i].get_image()
49+
page_update.emit()
4050

4151

4252
## Updates layer content with provided image.[br]
@@ -45,6 +55,7 @@ func set_content() -> void:
4555
func set_layer(idx: int, image: Image) -> void:
4656
layers[idx] = image
4757
textures[idx].update(layers[idx])
58+
page_update.emit()
4859

4960

5061
## Flattens the page into a single page. [br]
@@ -57,3 +68,23 @@ func flatten() -> Image:
5768
out.blend_rect(img, Rect2(Vector2.ZERO, img.get_size()), Vector2.ZERO)
5869
return out
5970
return null
71+
72+
73+
## Updates the name of a layer
74+
func rename(idx: int, new_name: String) -> void:
75+
names[idx] = new_name
76+
page_update.emit()
77+
78+
79+
func delete_layer(idx: int) -> void:
80+
layers.remove_at(idx)
81+
textures.remove_at(idx)
82+
names.remove_at(idx)
83+
page_update.emit()
84+
85+
86+
func insert_layer(idx: int, layer: Image, new_name: String) -> void:
87+
layers.insert(idx, layer)
88+
textures.insert(idx, ImageTexture.create_from_image(layers[idx]))
89+
names.insert(idx, new_name)
90+
page_update.emit()

system/project/project.gd

Lines changed: 57 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ class_name Project
22
extends Resource
33

44
signal new_current_page(page: Page)
5+
signal frames_update
56

67
@export var title: String = "New Animation"
78
@export var framerate: float = 1.0
@@ -18,8 +19,8 @@ signal new_current_page(page: Page)
1819

1920
@export var last_saved: String = "No Date"
2021

21-
var _current_layer: int = 0
22-
var _current_frame: int = 0
22+
var current_layer: int = 1
23+
var current_frame: int = 0
2324

2425

2526
## Initializes a new project. [br]
@@ -28,62 +29,64 @@ func new_project(w: int, h: int) -> void:
2829
width = w
2930
height = h
3031

31-
frames.append(Page.new(width, height))
32+
new_page()
3233
new_current_page.emit(frames[0])
34+
35+
frames_update.connect(_on_frame_update)
3336
print("[Project] New project created")
3437

3538

3639
## Returns the current focused page in the project.
3740
func get_current_page() -> Page:
38-
return frames[_current_frame]
41+
return frames[current_frame]
3942

4043

4144
## Sets the current frame to a specific index. [br]
4245
## Returns [code]null[/code] if the page does not exist. [br]
4346
## [param index]: The index of the page.
4447
func get_page_by_index(index: int = 0) -> Page:
4548
if index >= 0 and index < len(frames):
46-
_current_frame = index
47-
new_current_page.emit(frames[_current_frame])
48-
return frames[_current_frame]
49+
current_frame = index
50+
new_current_page.emit(frames[current_frame])
51+
return frames[current_frame]
4952
return null
5053

5154

5255
## Returns a page some distance removed from the focused page in the project. [br]
5356
## Returns [code]null[/code] if the page does not exist. [br]
5457
## [param distance]: Determines how far to grab the page.
5558
func get_distant_page(distance: int = 1) -> Page:
56-
if _current_frame + distance >= 0 and _current_frame + distance < len(frames):
57-
return frames[_current_frame + distance]
59+
if current_frame + distance >= 0 and current_frame + distance < len(frames):
60+
return frames[current_frame + distance]
5861
return null
5962

6063

6164
## Changes the current project page to the next page. [br]
6265
## [param is_loop]: Goes back to page 0 if at the end. Adds a new page otherwise.
6366
func next_page(is_loop: bool) -> Page:
64-
_current_frame += 1
65-
if len(frames) < (_current_frame + 1):
67+
current_frame += 1
68+
if len(frames) < (current_frame + 1):
6669
if is_loop:
67-
_current_frame = 0
70+
current_frame = 0
6871
else:
69-
frames.append(Page.new(width, height))
70-
new_current_page.emit(frames[_current_frame])
71-
return frames[_current_frame]
72+
new_page()
73+
new_current_page.emit(frames[current_frame])
74+
return frames[current_frame]
7275

7376

7477
## Changes the current project page to the previous page. Stops at frame 0.
7578
func prev_page() -> Page:
76-
if _current_frame > 0:
77-
_current_frame -= 1
78-
new_current_page.emit(frames[_current_frame])
79-
return frames[_current_frame]
79+
if current_frame > 0:
80+
current_frame -= 1
81+
new_current_page.emit(frames[current_frame])
82+
return frames[current_frame]
8083

8184

8285
## Triggers project saving elements.
8386
func on_project_save() -> void:
8487
last_saved = Time.get_datetime_string_from_system(false, true)
8588

86-
var tn = frames[_current_frame].flatten()
89+
var tn = frames[current_frame].flatten()
8790
if tn:
8891
tn.resize(128, 96, Image.INTERPOLATE_NEAREST)
8992
thumbnail = tn
@@ -95,3 +98,37 @@ func get_thumbnail() -> Texture2D:
9598
if img != null:
9699
return ImageTexture.create_from_image(img)
97100
return load("res://system/project/placeholder_thumbnail.png")
101+
102+
103+
## Emit signal when page updates
104+
func _on_page_update() -> void:
105+
frames_update.emit()
106+
107+
108+
## Create a new blank page
109+
func new_page() -> void:
110+
var pg := Page.new(width, height)
111+
pg.page_update.connect(_on_page_update)
112+
frames.append(pg)
113+
frames_update.emit()
114+
115+
116+
## Switch to new frame
117+
func set_frame(idx: int) -> void:
118+
current_frame = idx
119+
new_current_page.emit(frames[current_frame])
120+
121+
122+
func set_layer(idx: int) -> void:
123+
current_layer = idx
124+
125+
126+
func delete_frame(idx: int) -> void:
127+
frames.remove_at(idx)
128+
if current_frame >= frames.size():
129+
current_frame = frames.size() - 1
130+
new_current_page.emit(frames[current_frame])
131+
132+
133+
func _on_frame_update() -> void:
134+
new_current_page.emit(frames[current_frame])

tools/eraser/eraser.gd

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ func _place_stamp(_position: Vector2, _canvas: Canvas) -> void:
8383
)
8484

8585
var project: Project = _canvas._project
86-
var current_page: Page = project.frames[project._current_frame]
87-
var layer: Image = current_page.layers[project._current_layer + 1]
86+
var current_page: Page = project.frames[project.current_frame]
87+
var layer: Image = current_page.layers[project.current_layer]
8888

8989
var filter_width = img.get_width()
9090
var filter_height = img.get_height()
@@ -114,4 +114,4 @@ func _place_stamp(_position: Vector2, _canvas: Canvas) -> void:
114114
base.a = new_alpha
115115
layer.set_pixel(pixel_x, pixel_y, base)
116116

117-
current_page.set_layer(project._current_layer + 1, layer)
117+
current_page.set_layer(project.current_layer, layer)

tools/paint_bucket.gd

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ func _init() -> void:
1212

1313
func on_pointer_down(_position: Vector2, _canvas: Canvas) -> void:
1414
var project: Project = _canvas._project
15-
var current_page: Page = project.frames[project._current_frame]
16-
var layer: Image = current_page.layers[project._current_layer + 1]
15+
var current_page: Page = project.frames[project.current_frame]
16+
var layer: Image = current_page.layers[project.current_layer]
1717

1818
var directions = [Vector2i.DOWN, Vector2i.UP, Vector2i.LEFT, Vector2i.RIGHT]
1919

@@ -46,4 +46,4 @@ func on_pointer_down(_position: Vector2, _canvas: Canvas) -> void:
4646
):
4747
pixels.append(new_pixel)
4848
visited[new_pixel] = true
49-
current_page.set_layer(project._current_layer + 1, layer)
49+
current_page.set_layer(project.current_layer, layer)

views/editor/containers/page_controls/page_controls.gd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func attach_project(project: Project):
4242
func _update_view(_page: Page) -> void:
4343
framerate_slider.value = _project.framerate
4444
framerate_label.text = str(int(_project.framerate))
45-
page_count_label.text = "%d/%d" % [_project._current_frame + 1, _project.frames.size()]
45+
page_count_label.text = "%d/%d" % [_project.current_frame + 1, _project.frames.size()]
4646

4747

4848
func _on_next_button_pressed() -> void:
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
class_name CheckerBackground
2+
extends Control
3+
4+
@export var square_size: int = 3
5+
@export var color_a: Color = Color(0.75, 0.75, 0.75, 1.0)
6+
@export var color_b: Color = Color(0.95, 0.95, 0.95, 1.0)
7+
8+
9+
func _draw() -> void:
10+
var cols := ceili(size.x / square_size)
11+
var rows := ceili(size.y / square_size)
12+
13+
for y in range(rows):
14+
for x in range(cols):
15+
var use_a := (x + y) % 2 == 0
16+
var color := color_a if use_a else color_b
17+
18+
draw_rect(
19+
Rect2(Vector2(x * square_size, y * square_size), Vector2(square_size, square_size)),
20+
color
21+
)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
uid://bpj0rhv141ii4

0 commit comments

Comments
 (0)