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:
parent
2cdd426a45
commit
c5c421bd28
|
@ -1881,6 +1881,9 @@ msgstr ""
|
|||
msgid "Frame properties"
|
||||
msgstr ""
|
||||
|
||||
msgid "Layer properties"
|
||||
msgstr ""
|
||||
|
||||
msgid "Cel properties"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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="."]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"]
|
||||
|
|
78
src/UI/Timeline/LayerProperties.gd
Normal file
78
src/UI/Timeline/LayerProperties.gd
Normal 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
|
69
src/UI/Timeline/LayerProperties.tscn
Normal file
69
src/UI/Timeline/LayerProperties.tscn
Normal 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"]
|
Loading…
Reference in a new issue