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

Add layer properties

This commit is contained in:
Emmanouil Papadeas 2024-04-04 03:21:07 +03:00
parent 2cdd426a45
commit c5c421bd28
8 changed files with 192 additions and 7 deletions

View file

@ -1881,6 +1881,9 @@ msgstr ""
msgid "Frame properties"
msgstr ""
msgid "Layer properties"
msgstr ""
msgid "Cel properties"
msgstr ""

View file

@ -2,6 +2,8 @@ class_name BaseLayer
extends RefCounted
## Base class for layer properties. Different layer types extend from this class.
signal name_changed ## Emits when [member name] is changed.
## All currently supported layer blend modes between two layers. The upper layer
## is the blend layer, and the bottom layer is the base layer.
## For more information, refer to: [url]https://en.wikipedia.org/wiki/Blend_modes[/url]
@ -28,7 +30,10 @@ enum BlendModes {
LUMINOSITY ## Uses the blend luminosity while preserving the base hue and saturation.
}
var name := "" ## Name of the layer.
var name := "": ## Name of the layer.
set(value):
name = value
name_changed.emit()
var project: Project ## The project the layer belongs to.
var index: int ## Index of layer in the timeline.
var parent: BaseLayer ## Parent of the layer.
@ -41,6 +46,7 @@ var opacity := 1.0 ## The opacity of the layer, affects all frames that belong
var cel_link_sets: Array[Dictionary] = [] ## Each Dictionary represents a cel's "link set"
var effects: Array[LayerEffect] ## An array for non-destructive effects of the layer.
var effects_enabled := true ## If [code]true[/code], the effects are being applied.
var user_data := "" ## User defined data, set in the layer properties.
## Returns true if this is a direct or indirect parent of layer
@ -221,6 +227,8 @@ func serialize() -> Dictionary:
"parent": parent.index if is_instance_valid(parent) else -1,
"effects": effect_data
}
if not user_data.is_empty():
dict["user_data"] = user_data
if not cel_link_sets.is_empty():
var cels := [] # Cels array for easy finding of the frame index for link_set saving
for frame in project.frames:
@ -241,6 +249,7 @@ func deserialize(dict: Dictionary) -> void:
blend_mode = dict.get("blend_mode", BlendModes.NORMAL)
clipping_mask = dict.get("clipping_mask", false)
opacity = dict.get("opacity", 1.0)
user_data = dict.get("user_data", user_data)
if dict.get("parent", -1) != -1:
parent = project.layers[dict.parent]
if dict.has("linked_cels") and not dict["linked_cels"].is_empty(): # Backwards compatibility

View file

@ -1,4 +1,4 @@
[gd_scene load_steps=15 format=3 uid="uid://dbylw5k04ulp8"]
[gd_scene load_steps=16 format=3 uid="uid://dbylw5k04ulp8"]
[ext_resource type="Theme" uid="uid://cngbvfpwjoimv" path="res://assets/themes/dark/theme.tres" id="1"]
[ext_resource type="Script" path="res://src/Main.gd" id="2"]
@ -10,6 +10,7 @@
[ext_resource type="PackedScene" uid="uid://bs3dnnvnxyp68" path="res://src/UI/Timeline/FrameProperties.tscn" id="9"]
[ext_resource type="PackedScene" uid="uid://d4euwo633u33b" path="res://src/UI/Dialogs/SaveSprite.tscn" id="11"]
[ext_resource type="PackedScene" uid="uid://b3aeqj2k58wdk" path="res://src/UI/Dialogs/OpenSprite.tscn" id="12"]
[ext_resource type="PackedScene" uid="uid://d3dt1gdlf7hox" path="res://src/UI/Timeline/LayerProperties.tscn" id="13_4dhva"]
[ext_resource type="PackedScene" uid="uid://c0nuukjakmai2" path="res://src/UI/Dialogs/TileModeOffsetsDialog.tscn" id="14"]
[ext_resource type="Script" path="res://src/HandleExtensions.gd" id="15_v0k2h"]
[ext_resource type="PackedScene" uid="uid://clbjfkdupw52l" path="res://src/UI/Timeline/CelProperties.tscn" id="17_ucs64"]
@ -84,6 +85,8 @@ dialog_autowrap = true
[node name="FrameProperties" parent="Dialogs" instance=ExtResource("9")]
[node name="LayerProperties" parent="Dialogs" instance=ExtResource("13_4dhva")]
[node name="TileModeOffsetsDialog" parent="Dialogs" instance=ExtResource("14")]
[node name="Extensions" type="Control" parent="."]

View file

@ -42,7 +42,7 @@ var frame_tag_dialog: AcceptDialog:
@onready var move_down_layer := %MoveDownLayer as Button
@onready var merge_down_layer := %MergeDownLayer as Button
@onready var blend_modes_button := %BlendModes as OptionButton
@onready var opacity_slider: ValueSlider = %OpacitySlider
@onready var opacity_slider := %OpacitySlider as ValueSlider
@onready var frame_scroll_container := %FrameScrollContainer as Control
@onready var frame_scroll_bar := %FrameScrollBar as HScrollBar
@onready var tag_scroll_container := %TagScroll as ScrollContainer
@ -60,6 +60,7 @@ var frame_tag_dialog: AcceptDialog:
func _ready() -> void:
Global.control.find_child("LayerProperties").visibility_changed.connect(_update_layer_ui)
min_cel_size = get_tree().current_scene.theme.default_font_size + 24
layer_container.custom_minimum_size.x = layer_settings_container.size.x + 12
cel_size = min_cel_size
@ -1023,6 +1024,10 @@ func _on_onion_skinning_settings_visibility_changed() -> void:
func _cel_switched() -> void:
_toggle_frame_buttons()
_toggle_layer_buttons()
_update_layer_ui()
func _update_layer_ui() -> void:
var project := Global.current_project
var layer_opacity := project.layers[project.current_layer].opacity
opacity_slider.value = layer_opacity * 100

View file

@ -15,6 +15,7 @@ var button_pressed := false:
get:
return main_button.button_pressed
@onready var properties: AcceptDialog = Global.control.find_child("LayerProperties")
@onready var main_button := %LayerMainButton as Button
@onready var expand_button := %ExpandButton as BaseButton
@onready var visibility_button := %VisibilityButton as BaseButton
@ -32,6 +33,7 @@ func _ready() -> void:
main_button.hierarchy_depth_pixel_shift = HIERARCHY_DEPTH_PIXEL_SHIFT
Global.cel_switched.connect(func(): z_index = 1 if button_pressed else 0)
var layer := Global.current_project.layers[layer_index]
layer.name_changed.connect(func(): label.text = layer.name)
if layer is PixelLayer:
linked_button.visible = true
elif layer is GroupLayer:
@ -153,7 +155,6 @@ func _save_layer_name(new_name: String) -> void:
label.visible = true
line_edit.visible = false
line_edit.editable = false
label.text = new_name
if layer_index < Global.current_project.layers.size():
Global.current_project.layers[layer_index].name = new_name
@ -207,7 +208,22 @@ func _select_current_layer() -> void:
func _on_popup_menu_id_pressed(id: int) -> void:
var layer := Global.current_project.layers[layer_index]
if id == 0:
properties.layer_indices = _get_layer_indices()
properties.popup_centered()
if id == 1:
layer.clipping_mask = not layer.clipping_mask
popup_menu.set_item_checked(0, layer.clipping_mask)
clipping_mask_icon.visible = layer.clipping_mask
Global.canvas.draw_layers()
func _get_layer_indices() -> Array:
var indices := []
for cel in Global.current_project.selected_cels:
var l: int = cel[1]
if not l in indices:
indices.append(l)
indices.sort()
if not layer_index in indices:
indices = [layer_index]
return indices

View file

@ -163,10 +163,12 @@ caret_blink_interval = 0.5
[node name="PopupMenu" type="PopupMenu" parent="."]
disable_3d = true
item_count = 1
item_0/text = "Clipping mask"
item_0/checkable = 1
item_count = 2
item_0/text = "Properties"
item_0/id = 0
item_1/text = "Clipping mask"
item_1/checkable = 1
item_1/id = 1
[connection signal="pressed" from="VisibilityButton" to="." method="_on_visibility_button_pressed"]
[connection signal="pressed" from="LockButton" to="." method="_on_lock_button_pressed"]

View file

@ -0,0 +1,78 @@
extends AcceptDialog
var layer_indices: Array
@onready var name_line_edit := $GridContainer/NameLineEdit as LineEdit
@onready var opacity_slider := $GridContainer/OpacitySlider as ValueSlider
@onready var blend_modes_button := $GridContainer/BlendModeOptionButton as OptionButton
@onready var user_data_text_edit := $GridContainer/UserDataTextEdit as TextEdit
func _ready() -> void:
# Fill the blend modes OptionButton with items
blend_modes_button.add_item("Normal", BaseLayer.BlendModes.NORMAL)
blend_modes_button.add_item("Darken", BaseLayer.BlendModes.DARKEN)
blend_modes_button.add_item("Multiply", BaseLayer.BlendModes.MULTIPLY)
blend_modes_button.add_item("Color burn", BaseLayer.BlendModes.COLOR_BURN)
blend_modes_button.add_item("Linear burn", BaseLayer.BlendModes.LINEAR_BURN)
blend_modes_button.add_item("Lighten", BaseLayer.BlendModes.LIGHTEN)
blend_modes_button.add_item("Screen", BaseLayer.BlendModes.SCREEN)
blend_modes_button.add_item("Color dodge", BaseLayer.BlendModes.COLOR_DODGE)
blend_modes_button.add_item("Add", BaseLayer.BlendModes.ADD)
blend_modes_button.add_item("Overlay", BaseLayer.BlendModes.OVERLAY)
blend_modes_button.add_item("Soft light", BaseLayer.BlendModes.SOFT_LIGHT)
blend_modes_button.add_item("Hard light", BaseLayer.BlendModes.HARD_LIGHT)
blend_modes_button.add_item("Difference", BaseLayer.BlendModes.DIFFERENCE)
blend_modes_button.add_item("Exclusion", BaseLayer.BlendModes.EXCLUSION)
blend_modes_button.add_item("Subtract", BaseLayer.BlendModes.SUBTRACT)
blend_modes_button.add_item("Divide", BaseLayer.BlendModes.DIVIDE)
blend_modes_button.add_item("Hue", BaseLayer.BlendModes.HUE)
blend_modes_button.add_item("Saturation", BaseLayer.BlendModes.SATURATION)
blend_modes_button.add_item("Color", BaseLayer.BlendModes.COLOR)
blend_modes_button.add_item("Luminosity", BaseLayer.BlendModes.LUMINOSITY)
func _on_visibility_changed() -> void:
if layer_indices.size() == 0:
return
Global.dialog_open(visible)
var first_layer := Global.current_project.layers[layer_indices[0]]
if visible:
name_line_edit.text = first_layer.name
opacity_slider.value = first_layer.opacity * 100.0
blend_modes_button.selected = first_layer.blend_mode
user_data_text_edit.text = first_layer.user_data
else:
layer_indices = []
func _on_name_line_edit_text_changed(new_text: String) -> void:
if layer_indices.size() == 0:
return
for layer_index in layer_indices:
var layer := Global.current_project.layers[layer_index]
layer.name = new_text
func _on_opacity_slider_value_changed(value: float) -> void:
if layer_indices.size() == 0:
return
for layer_index in layer_indices:
var layer := Global.current_project.layers[layer_index]
layer.opacity = value / 100.0
Global.canvas.queue_redraw()
func _on_blend_mode_option_button_item_selected(index: BaseLayer.BlendModes) -> void:
if layer_indices.size() == 0:
return
for layer_index in layer_indices:
var layer := Global.current_project.layers[layer_index]
layer.blend_mode = index
Global.canvas.queue_redraw()
func _on_user_data_text_edit_text_changed() -> void:
for layer_index in layer_indices:
var layer := Global.current_project.layers[layer_index]
layer.user_data = user_data_text_edit.text

View file

@ -0,0 +1,69 @@
[gd_scene load_steps=3 format=3 uid="uid://d3dt1gdlf7hox"]
[ext_resource type="Script" path="res://src/UI/Timeline/LayerProperties.gd" id="1_54q1t"]
[ext_resource type="Script" path="res://src/UI/Nodes/ValueSlider.gd" id="2_bwpwc"]
[node name="LayerProperties" type="AcceptDialog"]
title = "Layer properties"
exclusive = false
popup_window = true
script = ExtResource("1_54q1t")
[node name="GridContainer" type="GridContainer" parent="."]
offset_right = 40.0
offset_bottom = 40.0
columns = 2
[node name="NameLabel" type="Label" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
text = "Name:"
[node name="NameLineEdit" type="LineEdit" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
[node name="OpacityLabel" type="Label" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
text = "Opacity:"
[node name="OpacitySlider" type="TextureProgressBar" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
focus_mode = 2
mouse_default_cursor_shape = 2
theme_type_variation = &"ValueSlider"
nine_patch_stretch = true
stretch_margin_left = 3
stretch_margin_top = 3
stretch_margin_right = 3
stretch_margin_bottom = 3
script = ExtResource("2_bwpwc")
[node name="BlendModeLabel" type="Label" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
text = "Blend mode:"
[node name="BlendModeOptionButton" type="OptionButton" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
mouse_default_cursor_shape = 2
[node name="UserDataLabel" type="Label" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 0
text = "User data:"
[node name="UserDataTextEdit" type="TextEdit" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
scroll_fit_content_height = true
[connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"]
[connection signal="text_changed" from="GridContainer/NameLineEdit" to="." method="_on_name_line_edit_text_changed"]
[connection signal="value_changed" from="GridContainer/OpacitySlider" to="." method="_on_opacity_slider_value_changed"]
[connection signal="item_selected" from="GridContainer/BlendModeOptionButton" to="." method="_on_blend_mode_option_button_item_selected"]
[connection signal="text_changed" from="GridContainer/UserDataTextEdit" to="." method="_on_user_data_text_edit_text_changed"]