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

Add user data for cels, frames and tags

Projects and layers are next, once I add project and layer setting dialogs respectively.
This commit is contained in:
Emmanouil Papadeas 2024-04-04 01:27:50 +03:00
parent bc4b4dff0f
commit 14a13a2161
10 changed files with 127 additions and 57 deletions

View file

@ -49,6 +49,7 @@ var name: String ## Name of tag
var color: Color ## Color of tag
var from: int ## First frame number in the tag (first frame in timeline is numbered 1)
var to: int ## First frame number in the tag (first frame in timeline is numbered 1)
var user_data := "" ## User defined data, set in the tag properties.
## Class Constructor (used as [code]AnimationTag.new(name, color, from, to)[/code])
@ -60,7 +61,10 @@ func _init(_name: String, _color: Color, _from: int, _to: int) -> void:
func serialize() -> Dictionary:
return {"name": name, "color": color.to_html(), "from": from, "to": to}
var dict := {"name": name, "color": color.to_html(), "from": from, "to": to}
if not user_data.is_empty():
dict["user_data"] = user_data
return dict
func get_size() -> int:

View file

@ -17,6 +17,7 @@ var transformed_content: Image ## Used in transformations (moving, scaling etc
## Used for individual cel ordering. Used for when cels need to be drawn above or below
## their corresponding layer.
var z_index := 0
var user_data := "" ## User defined data, set in the cel properties.
func get_final_opacity(layer: BaseLayer) -> float:
@ -78,14 +79,17 @@ func update_texture() -> void:
## Returns a curated [Dictionary] containing the cel data.
func serialize() -> Dictionary:
return {"opacity": opacity, "z_index": z_index}
var dict := {"opacity": opacity, "z_index": z_index}
if not user_data.is_empty():
dict["user_data"] = user_data
return dict
## Sets the cel data according to a curated [Dictionary] obtained from [method serialize].
func deserialize(dict: Dictionary) -> void:
opacity = dict["opacity"]
if dict.has("z_index"):
z_index = dict["z_index"]
z_index = dict.get("z_index", z_index)
user_data = dict.get("user_data", user_data)
## Used to perform cleanup after a cel is removed.

View file

@ -3,8 +3,9 @@ extends RefCounted
## A class for frame properties.
## A frame is a collection of cels, for each layer.
var cels: Array[BaseCel]
var duration := 1.0
var cels: Array[BaseCel] ## The array containing all of the frame's [BaseCel]s. One for each layer.
var duration := 1.0 ## The duration multiplier. This allows for individual frame timing.
var user_data := "" ## User defined data, set in the frame properties.
func _init(_cels: Array[BaseCel] = [], _duration := 1.0) -> void:

View file

@ -232,9 +232,12 @@ func serialize() -> Dictionary:
cel_data.append(cel.serialize())
cel_data[-1]["metadata"] = _serialize_metadata(cel)
frame_data.append(
{"cels": cel_data, "duration": frame.duration, "metadata": _serialize_metadata(frame)}
)
var current_frame_data := {
"cels": cel_data, "duration": frame.duration, "metadata": _serialize_metadata(frame)
}
if not frame.user_data.is_empty():
current_frame_data["user_data"] = frame.user_data
frame_data.append(current_frame_data)
var brush_data := []
for brush in brushes:
brush_data.append({"size_x": brush.get_size().x, "size_y": brush.get_size().y})
@ -335,6 +338,7 @@ func deserialize(dict: Dictionary, zip_reader: ZIPReader = null, file: FileAcces
duration = dict.frame_duration[frame_i]
var frame_class := Frame.new(cels, duration)
frame_class.user_data = frame.get("user_data", "")
_deserialize_metadata(frame_class, frame)
frames.append(frame_class)
frame_i += 1
@ -347,7 +351,9 @@ func deserialize(dict: Dictionary, zip_reader: ZIPReader = null, file: FileAcces
_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))
var new_tag := AnimationTag.new(tag.name, Color(tag.color), tag.from, tag.to)
new_tag.user_data = tag.get("user_data", "")
animation_tags.append(new_tag)
animation_tags = animation_tags
if dict.has("guides"):
for g in dict.guides:

View file

@ -6,6 +6,7 @@ var cel_indices: Array
@onready var layer_num := $GridContainer/LayerNum as Label
@onready var opacity_slider := $GridContainer/OpacitySlider as ValueSlider
@onready var z_index_slider := $GridContainer/ZIndexSlider as ValueSlider
@onready var user_data_text_edit := $GridContainer/UserDataTextEdit as TextEdit
func _on_visibility_changed() -> void:
@ -25,6 +26,7 @@ func _on_visibility_changed() -> void:
layer_num.text = "[%s...%s]" % [first_layer.name, last_layer.name]
opacity_slider.value = first_cel.opacity * 100.0
z_index_slider.value = first_cel.z_index
user_data_text_edit.text = first_cel.user_data
else:
cel_indices = []
@ -47,3 +49,9 @@ func _on_z_index_slider_value_changed(value: float) -> void:
Global.current_project.order_layers()
Global.canvas.update_all_layers = true
Global.canvas.queue_redraw()
func _on_user_data_text_edit_text_changed() -> void:
for cel_index in cel_indices:
var cel := Global.current_project.frames[cel_index[0]].cels[cel_index[1]]
cel.user_data = user_data_text_edit.text

View file

@ -5,7 +5,7 @@
[node name="CelProperties" type="AcceptDialog"]
title = "Cel properties"
size = Vector2i(300, 161)
size = Vector2i(300, 188)
exclusive = false
popup_window = true
script = ExtResource("1_lyy7i")
@ -14,7 +14,7 @@ script = ExtResource("1_lyy7i")
offset_left = 8.0
offset_top = 8.0
offset_right = 292.0
offset_bottom = 112.0
offset_bottom = 139.0
columns = 2
[node name="Frame" type="Label" parent="GridContainer"]
@ -76,6 +76,15 @@ stretch_margin_right = 3
stretch_margin_bottom = 3
script = ExtResource("1_85pb7")
[node name="UserDataLabel" type="Label" parent="GridContainer"]
layout_mode = 2
text = "User data:"
[node name="UserDataTextEdit" type="TextEdit" parent="GridContainer"]
layout_mode = 2
scroll_fit_content_height = true
[connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"]
[connection signal="value_changed" from="GridContainer/OpacitySlider" to="." method="_on_opacity_slider_value_changed"]
[connection signal="value_changed" from="GridContainer/ZIndexSlider" to="." method="_on_z_index_slider_value_changed"]
[connection signal="text_changed" from="GridContainer/UserDataTextEdit" to="." method="_on_user_data_text_edit_text_changed"]

View file

@ -3,6 +3,7 @@ extends ConfirmationDialog
var frame_indices := []
@onready var frame_num := $GridContainer/FrameNum
@onready var frame_dur := $GridContainer/FrameTime
@onready var user_data_text_edit := $GridContainer/UserDataTextEdit as TextEdit
func _on_FrameProperties_about_to_show() -> void:
@ -13,8 +14,10 @@ func _on_FrameProperties_about_to_show() -> void:
frame_num.set_text(str(frame_indices[0] + 1))
else:
frame_num.set_text("[%s...%s]" % [frame_indices[0] + 1, frame_indices[-1] + 1])
var duration: float = Global.current_project.frames[frame_indices[0]].duration
var frame := Global.current_project.frames[frame_indices[0]]
var duration := frame.duration
frame_dur.set_value(duration)
user_data_text_edit.text = frame.user_data
func _on_FrameProperties_visibility_changed() -> void:
@ -24,13 +27,15 @@ func _on_FrameProperties_visibility_changed() -> void:
func _on_FrameProperties_confirmed() -> void:
var project := Global.current_project
var new_duration: float = frame_dur.get_value()
var new_user_data := user_data_text_edit.text
project.undos += 1
project.undo_redo.create_action("Change frame duration")
for frame in frame_indices:
project.undo_redo.add_do_property(project.frames[frame], "duration", new_duration)
project.undo_redo.add_undo_property(
project.frames[frame], "duration", project.frames[frame].duration
)
for frame_idx in frame_indices:
var frame := project.frames[frame_idx]
project.undo_redo.add_do_property(frame, "duration", new_duration)
project.undo_redo.add_do_property(frame, "user_data", new_user_data)
project.undo_redo.add_undo_property(frame, "duration", frame.duration)
project.undo_redo.add_undo_property(frame, "user_data", frame.user_data)
project.undo_redo.add_do_method(Global.undo_or_redo.bind(false))
project.undo_redo.add_undo_method(Global.undo_or_redo.bind(true))
project.undo_redo.commit_action()

View file

@ -38,6 +38,14 @@ value = 1.0
allow_greater = true
suffix = "x"
[node name="UserDataLabel" type="Label" parent="GridContainer"]
layout_mode = 2
text = "User data:"
[node name="UserDataTextEdit" type="TextEdit" parent="GridContainer"]
layout_mode = 2
scroll_fit_content_height = true
[connection signal="about_to_popup" from="." to="." method="_on_FrameProperties_about_to_show"]
[connection signal="confirmed" from="." to="." method="_on_FrameProperties_confirmed"]
[connection signal="visibility_changed" from="." to="." method="_on_FrameProperties_visibility_changed"]

View file

@ -7,6 +7,11 @@ var delete_tag_button: Button
@onready var main_vbox_cont: VBoxContainer = $VBoxContainer/ScrollContainer/VBoxTagContainer
@onready var add_tag_button: Button = $VBoxContainer/ScrollContainer/VBoxTagContainer/AddTag
@onready var options_dialog := $TagOptions
@onready var name_line_edit := $TagOptions/GridContainer/NameLineEdit as LineEdit
@onready var color_picker_button := $TagOptions/GridContainer/ColorPickerButton as ColorPickerButton
@onready var from_spinbox := $TagOptions/GridContainer/FromSpinBox as SpinBox
@onready var to_spinbox := $TagOptions/GridContainer/ToSpinBox as SpinBox
@onready var user_data_text_edit := $TagOptions/GridContainer/UserDataTextEdit as TextEdit
func _ready() -> void:
@ -71,11 +76,10 @@ func _on_AddTag_pressed() -> void:
frames.append(cel[0])
frames.sort()
options_dialog.get_node("GridContainer/FromSpinBox").value = (frames[0] + 1)
options_dialog.get_node("GridContainer/ToSpinBox").value = (frames[-1] + 1)
options_dialog.get_node("GridContainer/ColorPickerButton").color = Color(
randf(), randf(), randf()
)
from_spinbox.value = (frames[0] + 1)
to_spinbox.value = (frames[-1] + 1)
color_picker_button.color = Color(randf(), randf(), randf())
user_data_text_edit.text = ""
func _on_EditButton_pressed(_tag_id: int, edit_button: Button) -> void:
@ -83,11 +87,12 @@ func _on_EditButton_pressed(_tag_id: int, edit_button: Button) -> void:
var y_pos := edit_button.global_position.y + 2 * edit_button.size.y
options_dialog.popup(Rect2i(position + Vector2i(x_pos, y_pos), options_dialog.size))
current_tag_id = _tag_id
var animation_tag: AnimationTag = Global.current_project.animation_tags[_tag_id]
options_dialog.get_node("GridContainer/NameLineEdit").text = animation_tag.name
options_dialog.get_node("GridContainer/ColorPickerButton").color = animation_tag.color
options_dialog.get_node("GridContainer/FromSpinBox").value = animation_tag.from
options_dialog.get_node("GridContainer/ToSpinBox").value = animation_tag.to
var animation_tag := Global.current_project.animation_tags[_tag_id]
name_line_edit.text = animation_tag.name
color_picker_button.color = animation_tag.color
from_spinbox.value = animation_tag.from
to_spinbox.value = animation_tag.to
user_data_text_edit.text = animation_tag.user_data
if !delete_tag_button:
delete_tag_button = options_dialog.add_button("Delete", true, "delete_tag")
else:
@ -95,10 +100,11 @@ func _on_EditButton_pressed(_tag_id: int, edit_button: Button) -> void:
func _on_TagOptions_confirmed() -> void:
var tag_name: String = options_dialog.get_node("GridContainer/NameLineEdit").text
var tag_color: Color = options_dialog.get_node("GridContainer/ColorPickerButton").color
var tag_from: int = options_dialog.get_node("GridContainer/FromSpinBox").value
var tag_to: int = options_dialog.get_node("GridContainer/ToSpinBox").value
var tag_name := name_line_edit.text
var tag_color := color_picker_button.color
var tag_from := from_spinbox.value
var tag_to := to_spinbox.value
var user_data := user_data_text_edit.text
if tag_to > Global.current_project.frames.size():
tag_to = Global.current_project.frames.size()
@ -110,20 +116,22 @@ func _on_TagOptions_confirmed() -> void:
# 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():
var prev_tag: AnimationTag = new_animation_tags[i]
new_animation_tags[i] = AnimationTag.new(
new_animation_tags[i].name,
new_animation_tags[i].color,
new_animation_tags[i].from,
new_animation_tags[i].to
prev_tag.name, prev_tag.color, prev_tag.from, prev_tag.to
)
new_animation_tags[i].user_data = prev_tag.user_data
if current_tag_id == Global.current_project.animation_tags.size():
new_animation_tags.append(AnimationTag.new(tag_name, tag_color, tag_from, tag_to))
var new_tag := AnimationTag.new(tag_name, tag_color, tag_from, tag_to)
new_tag.user_data = user_data
new_animation_tags.append(new_tag)
else:
new_animation_tags[current_tag_id].name = tag_name
new_animation_tags[current_tag_id].color = tag_color
new_animation_tags[current_tag_id].from = tag_from
new_animation_tags[current_tag_id].to = tag_to
new_animation_tags[current_tag_id].user_data = user_data
# Handle Undo/Redo
Global.current_project.undos += 1
@ -141,24 +149,25 @@ func _on_TagOptions_confirmed() -> void:
func _on_TagOptions_custom_action(action: String) -> void:
if action == "delete_tag":
var new_animation_tags := Global.current_project.animation_tags.duplicate()
new_animation_tags.remove_at(current_tag_id)
# Handle Undo/Redo
Global.current_project.undos += 1
Global.current_project.undo_redo.create_action("Delete Frame Tag")
Global.current_project.undo_redo.add_do_method(Global.general_redo)
Global.current_project.undo_redo.add_undo_method(Global.general_undo)
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, "animation_tags", Global.current_project.animation_tags
)
Global.current_project.undo_redo.commit_action()
if action != "delete_tag":
return
var new_animation_tags := Global.current_project.animation_tags.duplicate()
new_animation_tags.remove_at(current_tag_id)
# Handle Undo/Redo
Global.current_project.undos += 1
Global.current_project.undo_redo.create_action("Delete Frame Tag")
Global.current_project.undo_redo.add_do_method(Global.general_redo)
Global.current_project.undo_redo.add_undo_method(Global.general_undo)
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, "animation_tags", Global.current_project.animation_tags
)
Global.current_project.undo_redo.commit_action()
options_dialog.hide()
_on_FrameTagDialog_about_to_show()
options_dialog.hide()
_on_FrameTagDialog_about_to_show()
func _on_TagOptions_visibility_changed() -> void:

View file

@ -63,7 +63,7 @@ button_pressed = true
text = "Animation plays only on frames of the same tag"
[node name="TagOptions" type="ConfirmationDialog" parent="."]
size = Vector2i(303, 127)
size = Vector2i(303, 240)
exclusive = false
popup_window = true
@ -71,35 +71,41 @@ popup_window = true
offset_left = 8.0
offset_top = 8.0
offset_right = 295.0
offset_bottom = 78.0
offset_bottom = 191.0
theme_override_constants/h_separation = 8
theme_override_constants/v_separation = 8
columns = 4
columns = 2
[node name="NameLabel" type="Label" parent="TagOptions/GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
text = "Name:"
[node name="NameLineEdit" type="LineEdit" parent="TagOptions/GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
caret_blink = true
caret_blink_interval = 0.5
[node name="ColorLabel" type="Label" parent="TagOptions/GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
text = "Color:"
[node name="ColorPickerButton" type="ColorPickerButton" parent="TagOptions/GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
mouse_default_cursor_shape = 2
color = Color(1, 0, 0, 1)
[node name="FromLabel" type="Label" parent="TagOptions/GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
text = "From:"
[node name="FromSpinBox" type="SpinBox" parent="TagOptions/GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
mouse_default_cursor_shape = 2
min_value = 1.0
value = 1.0
@ -107,15 +113,25 @@ allow_greater = true
[node name="ToLabel" type="Label" parent="TagOptions/GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
text = "To:"
[node name="ToSpinBox" type="SpinBox" parent="TagOptions/GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
mouse_default_cursor_shape = 2
min_value = 1.0
value = 1.0
allow_greater = true
[node name="UserDataLabel" type="Label" parent="TagOptions/GridContainer"]
layout_mode = 2
text = "User data:"
[node name="UserDataTextEdit" type="TextEdit" parent="TagOptions/GridContainer"]
layout_mode = 2
scroll_fit_content_height = true
[connection signal="about_to_popup" from="." to="." method="_on_FrameTagDialog_about_to_show"]
[connection signal="visibility_changed" from="." to="." method="_on_FrameTagDialog_visibility_changed"]
[connection signal="pressed" from="VBoxContainer/ScrollContainer/VBoxTagContainer/AddTag" to="." method="_on_AddTag_pressed"]