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:
parent
bc4b4dff0f
commit
14a13a2161
|
@ -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:
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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,7 +149,8 @@ func _on_TagOptions_confirmed() -> void:
|
|||
|
||||
|
||||
func _on_TagOptions_custom_action(action: String) -> void:
|
||||
if action == "delete_tag":
|
||||
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
|
||||
|
|
|
@ -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"]
|
||||
|
|
Loading…
Reference in a new issue