From d3be746290bac7e4d747893d284b3bd63546090d Mon Sep 17 00:00:00 2001 From: Emmanouil Papadeas Date: Thu, 14 Mar 2024 20:13:42 +0200 Subject: [PATCH] Change UI of layer buttons - bring the small buttons out of the main button This makes the visible/lock/linked/expand buttons more clear that they are indeed buttons. This commit also slightly changes the look of child layers, as they only appear intended and they no longer change color. --- src/UI/Timeline/LayerButton.gd | 230 ++++------------------------- src/UI/Timeline/LayerButton.tscn | 148 +++++++++---------- src/UI/Timeline/LayerMainButton.gd | 171 +++++++++++++++++++++ 3 files changed, 264 insertions(+), 285 deletions(-) create mode 100644 src/UI/Timeline/LayerMainButton.gd diff --git a/src/UI/Timeline/LayerButton.gd b/src/UI/Timeline/LayerButton.gd index d31d3fc30..812ea95cc 100644 --- a/src/UI/Timeline/LayerButton.gd +++ b/src/UI/Timeline/LayerButton.gd @@ -1,10 +1,21 @@ class_name LayerButton -extends Button +extends HBoxContainer -const HIERARCHY_DEPTH_PIXEL_SHIFT := 8 +const HIERARCHY_DEPTH_PIXEL_SHIFT := 16 -var layer_index := 0 +var layer_index := 0: + set(value): + layer_index = value + if is_instance_valid(main_button): + main_button.layer_index = value +var button_pressed := false: + set(value): + button_pressed = value + main_button.button_pressed = value + get: + return main_button.button_pressed +@onready var main_button := %LayerMainButton as Button @onready var expand_button := %ExpandButton as BaseButton @onready var visibility_button := %VisibilityButton as BaseButton @onready var lock_button := %LockButton as BaseButton @@ -17,6 +28,8 @@ var layer_index := 0 func _ready() -> void: + main_button.layer_index = layer_index + 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] if layer is PixelLayer: @@ -24,27 +37,20 @@ func _ready() -> void: elif layer is GroupLayer: expand_button.visible = true custom_minimum_size.y = Global.animation_timeline.cel_size - label.text = layer.name line_edit.text = layer.name - - var layer_buttons := find_child("LayerButtons") - for child in layer_buttons.get_children(): + for child in get_children(): + if not child is Button: + continue var texture = child.get_child(0) + if not texture is TextureRect: + continue texture.modulate = Global.modulate_icon_color # Visualize how deep into the hierarchy the layer is var hierarchy_depth := layer.get_hierarchy_depth() hierarchy_spacer.custom_minimum_size.x = hierarchy_depth * HIERARCHY_DEPTH_PIXEL_SHIFT - - if Global.control.theme.get_color("font_color", "Button").v > 0.5: # Light text is dark theme - self_modulate.v = 1 + hierarchy_depth * 0.4 - else: # Dark text should be light theme - self_modulate.v = 1 - hierarchy_depth * 0.075 - update_buttons() - await get_tree().process_frame - queue_redraw() func update_buttons() -> void: @@ -92,14 +98,6 @@ func _update_buttons_all_layers() -> void: Global.cel_vbox.get_child(layer_button.get_index()).visible = expanded -func _draw() -> void: - if hierarchy_spacer.size.x > 0.1: - var color := Color(1, 1, 1, 0.33) - color.v = roundf(Global.control.theme.get_color("font_color", "Button").v) - var x := hierarchy_spacer.global_position.x - global_position.x + hierarchy_spacer.size.x - draw_line(Vector2(x, 0), Vector2(x, size.y), color) - - func _input(event: InputEvent) -> void: if ( (event.is_action_released(&"ui_accept") or event.is_action_released(&"ui_cancel")) @@ -109,7 +107,7 @@ func _input(event: InputEvent) -> void: _save_layer_name(line_edit.text) -func _on_LayerContainer_gui_input(event: InputEvent) -> void: +func _on_main_button_gui_input(event: InputEvent) -> void: var project := Global.current_project if not event is InputEventMouseButton: @@ -147,7 +145,7 @@ func _on_LayerContainer_gui_input(event: InputEvent) -> void: popup_menu.popup(Rect2(get_global_mouse_position(), Vector2.ONE)) -func _on_LineEdit_focus_exited() -> void: +func _on_layer_name_line_edit_focus_exited() -> void: _save_layer_name(line_edit.text) @@ -160,13 +158,13 @@ func _save_layer_name(new_name: String) -> void: Global.current_project.layers[layer_index].name = new_name -func _on_ExpandButton_pressed() -> void: +func _on_expand_button_pressed() -> void: var layer := Global.current_project.layers[layer_index] layer.expanded = !layer.expanded _update_buttons_all_layers() -func _on_VisibilityButton_pressed() -> void: +func _on_visibility_button_pressed() -> void: Global.canvas.selection.transform_content_confirm() var layer := Global.current_project.layers[layer_index] layer.visible = !layer.visible @@ -177,7 +175,7 @@ func _on_VisibilityButton_pressed() -> void: _update_buttons_all_layers() -func _on_LockButton_pressed() -> void: +func _on_lock_button_pressed() -> void: Global.canvas.selection.transform_content_confirm() var layer := Global.current_project.layers[layer_index] layer.locked = !layer.locked @@ -186,7 +184,7 @@ func _on_LockButton_pressed() -> void: _update_buttons_all_layers() -func _on_LinkButton_pressed() -> void: +func _on_link_button_pressed() -> void: Global.canvas.selection.transform_content_confirm() var layer := Global.current_project.layers[layer_index] if not layer is PixelLayer: @@ -206,180 +204,6 @@ func _select_current_layer() -> void: Global.current_project.change_cel(-1, layer_index) -func _get_drag_data(_position: Vector2) -> Variant: - var layer := Global.current_project.layers[layer_index] - var layers := range(layer_index - layer.get_child_count(true), layer_index + 1) - - var box := VBoxContainer.new() - for i in layers.size(): - var button := Button.new() - button.custom_minimum_size = size - button.theme = Global.control.theme - button.text = Global.current_project.layers[layers[-1 - i]].name - box.add_child(button) - set_drag_preview(box) - - return ["Layer", layer_index] - - -func _can_drop_data(_pos: Vector2, data) -> bool: - if typeof(data) != TYPE_ARRAY: - Global.animation_timeline.drag_highlight.visible = false - return false - if data[0] != "Layer": - Global.animation_timeline.drag_highlight.visible = false - return false - var curr_layer: BaseLayer = Global.current_project.layers[layer_index] - var drag_layer: BaseLayer = Global.current_project.layers[data[1]] - - if curr_layer == drag_layer: - Global.animation_timeline.drag_highlight.visible = false - return false - - var region: Rect2 - var depth := curr_layer.get_hierarchy_depth() - - if Input.is_action_pressed(&"ctrl"): # Swap layers - if drag_layer.is_ancestor_of(curr_layer) or curr_layer.is_ancestor_of(drag_layer): - Global.animation_timeline.drag_highlight.visible = false - return false - region = get_global_rect() - - else: # Shift layers - if drag_layer.is_ancestor_of(curr_layer): - Global.animation_timeline.drag_highlight.visible = false - return false - # If accepted as a child, is it in the center region? - if ( - curr_layer.accepts_child(drag_layer) - and _get_region_rect(0.25, 0.75).has_point(get_global_mouse_position()) - ): - # Drawn regions are adjusted a bit from actual to clarify drop position - region = _get_region_rect(0.15, 0.85) - depth += 1 - else: - # Top or bottom region? - if _get_region_rect(0, 0.5).has_point(get_global_mouse_position()): - region = _get_region_rect(-0.1, 0.15) - else: - region = _get_region_rect(0.85, 1.1) - # Shift drawn region to the right a bit for hierarchy depth visualization: - region.position.x += depth * HIERARCHY_DEPTH_PIXEL_SHIFT - region.size.x -= depth * HIERARCHY_DEPTH_PIXEL_SHIFT - Global.animation_timeline.drag_highlight.global_position = region.position - Global.animation_timeline.drag_highlight.size = region.size - Global.animation_timeline.drag_highlight.visible = true - return true - - -func _drop_data(_pos: Vector2, data) -> void: - var drop_layer: int = data[1] - var project := Global.current_project - - project.undo_redo.create_action("Change Layer Order") - var layers: Array = project.layers # This shouldn't be modified directly - - var drop_from_indices := range( - drop_layer - layers[drop_layer].get_child_count(true), drop_layer + 1 - ) - - var drop_from_parents := [] - for i in range(drop_from_indices.size()): - drop_from_parents.append(layers[drop_from_indices[i]].parent) - - if Input.is_action_pressed("ctrl"): # Swap layers - # a and b both need "from", "to", and "to_parents" - # a is this layer (and children), b is the dropped layers - var a := { - "from": range(layer_index - layers[layer_index].get_child_count(true), layer_index + 1) - } - var b := {"from": drop_from_indices} - - if a.from[0] < b.from[0]: - a["to"] = range(b.from[-1] + 1 - a.from.size(), b.from[-1] + 1) # Size of a, start from end of b - b["to"] = range(a.from[0], a.from[0] + b.from.size()) # Size of b, start from beginning of a - else: - a["to"] = range(b.from[0], b.from[0] + a.from.size()) # Size of a, start from beginning of b - b["to"] = range(a.from[-1] + 1 - b.from.size(), a.from[-1] + 1) # Size of b, start from end of a - - var a_from_parents := [] - for l in a.from: - a_from_parents.append(layers[l].parent) - - # to_parents starts as a dulpicate of from_parents, set the root layer's (with one layer or - # group with its children, this will always be the last layer [-1]) parent to the other - # root layer's parent - a["to_parents"] = a_from_parents.duplicate() - b["to_parents"] = drop_from_parents.duplicate() - a.to_parents[-1] = drop_from_parents[-1] - b.to_parents[-1] = a_from_parents[-1] - - project.undo_redo.add_do_method(project.swap_layers.bind(a, b)) - project.undo_redo.add_undo_method( - project.swap_layers.bind( - {"from": a.to, "to": a.from, "to_parents": a_from_parents}, - {"from": b.to, "to": drop_from_indices, "to_parents": drop_from_parents} - ) - ) - - else: # Move layers - var to_index: int # the index where the LOWEST moved layer should end up - var to_parent: BaseLayer - - # If accepted as a child, is it in the center region? - if ( - layers[layer_index].accepts_child(layers[drop_layer]) - and _get_region_rect(0.25, 0.75).has_point(get_global_mouse_position()) - ): - to_index = layer_index - to_parent = layers[layer_index] - else: - # Top or bottom region? - if _get_region_rect(0, 0.5).has_point(get_global_mouse_position()): - to_index = layer_index + 1 - to_parent = layers[layer_index].parent - else: - # Place under the layer, if it has children, place after its lowest child - if layers[layer_index].has_children(): - to_index = layers[layer_index].get_children(true)[0].index - - if layers[layer_index].is_ancestor_of(layers[drop_layer]): - to_index += drop_from_indices.size() - else: - to_index = layer_index - to_parent = layers[layer_index].parent - - if drop_layer < layer_index: - to_index -= drop_from_indices.size() - - var drop_to_indices := range(to_index, to_index + drop_from_indices.size()) - - var to_parents := drop_from_parents.duplicate() - to_parents[-1] = to_parent - - project.undo_redo.add_do_method( - project.move_layers.bind(drop_from_indices, drop_to_indices, to_parents) - ) - project.undo_redo.add_undo_method( - project.move_layers.bind(drop_to_indices, drop_from_indices, drop_from_parents) - ) - if project.current_layer == drop_layer: - project.undo_redo.add_do_method(project.change_cel.bind(-1, layer_index)) - else: - project.undo_redo.add_do_method(project.change_cel.bind(-1, project.current_layer)) - project.undo_redo.add_undo_method(project.change_cel.bind(-1, project.current_layer)) - project.undo_redo.add_undo_method(Global.undo_or_redo.bind(true)) - project.undo_redo.add_do_method(Global.undo_or_redo.bind(false)) - project.undo_redo.commit_action() - - -func _get_region_rect(y_begin: float, y_end: float) -> Rect2: - var rect := get_global_rect() - rect.position.y += rect.size.y * y_begin - rect.size.y *= y_end - y_begin - return rect - - func _on_popup_menu_id_pressed(id: int) -> void: var layer := Global.current_project.layers[layer_index] if id == 0: diff --git a/src/UI/Timeline/LayerButton.tscn b/src/UI/Timeline/LayerButton.tscn index 9a5f913b8..2f8200893 100644 --- a/src/UI/Timeline/LayerButton.tscn +++ b/src/UI/Timeline/LayerButton.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=7 format=3 uid="uid://bai814sqvk68f"] +[gd_scene load_steps=8 format=3 uid="uid://bai814sqvk68f"] [ext_resource type="Script" path="res://src/UI/Timeline/LayerButton.gd" id="1_6hlpe"] [ext_resource type="Texture2D" uid="uid://c2b3htff5yox8" path="res://assets/graphics/layers/layer_visible.png" id="2_ef6fb"] @@ -6,72 +6,27 @@ [ext_resource type="Texture2D" uid="uid://dhc0pnnqojd2m" path="res://assets/graphics/layers/unlock.png" id="3_ah1my"] [ext_resource type="Texture2D" uid="uid://cofw1x6chh4i" path="res://assets/graphics/layers/unlinked_layer.png" id="4_058qm"] [ext_resource type="Texture2D" uid="uid://ieo8fsapcgsy" path="res://assets/graphics/layers/clipping_mask.png" id="6_73j5q"] +[ext_resource type="Script" path="res://src/UI/Timeline/LayerMainButton.gd" id="6_n8q6b"] -[node name="LayerButton" type="Button"] -offset_right = 200.0 -offset_bottom = 36.0 -focus_mode = 0 -mouse_default_cursor_shape = 2 -theme_type_variation = &"LayerFrameButton" -toggle_mode = true -action_mode = 0 -script = ExtResource("1_6hlpe") - -[node name="HBoxContainer" type="HBoxContainer" parent="."] -layout_mode = 1 +[node name="LayerButton" type="HBoxContainer"] anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -size_flags_horizontal = 0 +size_flags_horizontal = 3 +theme_override_constants/separation = 0 +script = ExtResource("1_6hlpe") -[node name="EmptySpacer" type="Control" parent="HBoxContainer"] -layout_mode = 2 -mouse_filter = 2 - -[node name="LayerButtons" type="HBoxContainer" parent="HBoxContainer"] -custom_minimum_size = Vector2(118, 0) -layout_mode = 2 -theme_override_constants/separation = 10 - -[node name="ExpandButton" type="Button" parent="HBoxContainer/LayerButtons" groups=["UIButtons"]] +[node name="VisibilityButton" type="Button" parent="." groups=["UIButtons"]] unique_name_in_owner = true -visible = false -custom_minimum_size = Vector2(22, 22) +custom_minimum_size = Vector2(28, 22) layout_mode = 2 -size_flags_horizontal = 0 -size_flags_vertical = 4 -tooltip_text = "Expand/collapse group" -mouse_default_cursor_shape = 2 -flat = true - -[node name="TextureRect" type="TextureRect" parent="HBoxContainer/LayerButtons/ExpandButton"] -layout_mode = 0 -anchor_left = 0.5 -anchor_top = 0.5 -anchor_right = 0.5 -anchor_bottom = 0.5 -offset_left = -11.0 -offset_top = -11.0 -offset_right = 11.0 -offset_bottom = 11.0 -size_flags_horizontal = 0 -size_flags_vertical = 0 -texture = ExtResource("2_enrtd") - -[node name="VisibilityButton" type="Button" parent="HBoxContainer/LayerButtons" groups=["UIButtons"]] -unique_name_in_owner = true -custom_minimum_size = Vector2(22, 22) -layout_mode = 2 -size_flags_horizontal = 0 -size_flags_vertical = 4 tooltip_text = "Toggle layer's visibility" focus_mode = 0 mouse_default_cursor_shape = 2 -flat = true -[node name="TextureRect" type="TextureRect" parent="HBoxContainer/LayerButtons/VisibilityButton"] +[node name="TextureRect" type="TextureRect" parent="VisibilityButton"] layout_mode = 0 anchor_left = 0.5 anchor_top = 0.5 @@ -85,18 +40,15 @@ size_flags_horizontal = 0 size_flags_vertical = 0 texture = ExtResource("2_ef6fb") -[node name="LockButton" type="Button" parent="HBoxContainer/LayerButtons" groups=["UIButtons"]] +[node name="LockButton" type="Button" parent="." groups=["UIButtons"]] unique_name_in_owner = true -custom_minimum_size = Vector2(22, 22) +custom_minimum_size = Vector2(28, 22) layout_mode = 2 -size_flags_horizontal = 0 -size_flags_vertical = 4 tooltip_text = "Lock/unlock layer" focus_mode = 0 mouse_default_cursor_shape = 2 -flat = true -[node name="TextureRect" type="TextureRect" parent="HBoxContainer/LayerButtons/LockButton"] +[node name="TextureRect" type="TextureRect" parent="LockButton"] layout_mode = 0 anchor_left = 0.5 anchor_top = 0.5 @@ -110,20 +62,18 @@ size_flags_horizontal = 0 size_flags_vertical = 0 texture = ExtResource("3_ah1my") -[node name="LinkButton" type="Button" parent="HBoxContainer/LayerButtons" groups=["UIButtons"]] +[node name="LinkButton" type="Button" parent="." groups=["UIButtons"]] unique_name_in_owner = true visible = false -custom_minimum_size = Vector2(22, 22) +custom_minimum_size = Vector2(28, 22) layout_mode = 2 -size_flags_horizontal = 0 -size_flags_vertical = 4 tooltip_text = "Enable/disable automatic linking of new cels when creating new frames Linked cels share content across multiple frames" +focus_mode = 0 mouse_default_cursor_shape = 2 -flat = true -[node name="TextureRect" type="TextureRect" parent="HBoxContainer/LayerButtons/LinkButton"] +[node name="TextureRect" type="TextureRect" parent="LinkButton"] layout_mode = 0 anchor_left = 0.5 anchor_top = 0.5 @@ -137,32 +87,70 @@ size_flags_horizontal = 0 size_flags_vertical = 0 texture = ExtResource("4_058qm") -[node name="LayerName" type="HBoxContainer" parent="HBoxContainer"] +[node name="ExpandButton" type="Button" parent="." groups=["UIButtons"]] +unique_name_in_owner = true +visible = false +custom_minimum_size = Vector2(28, 22) +layout_mode = 2 +tooltip_text = "Expand/collapse group" +focus_mode = 0 +mouse_default_cursor_shape = 2 + +[node name="TextureRect" type="TextureRect" parent="ExpandButton"] +layout_mode = 0 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -11.0 +offset_top = -11.0 +offset_right = 11.0 +offset_bottom = 11.0 +size_flags_horizontal = 0 +size_flags_vertical = 0 +texture = ExtResource("2_enrtd") + +[node name="LayerMainButton" type="Button" parent="."] +unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 +focus_mode = 0 mouse_default_cursor_shape = 2 -alignment = 1 +theme_type_variation = &"LayerFrameButton" +toggle_mode = true +action_mode = 0 +script = ExtResource("6_n8q6b") -[node name="HierarchySpacer" type="Control" parent="HBoxContainer/LayerName"] +[node name="LayerName" type="HBoxContainer" parent="LayerMainButton"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +mouse_default_cursor_shape = 2 + +[node name="HierarchySpacer" type="Control" parent="LayerMainButton/LayerName"] unique_name_in_owner = true layout_mode = 2 mouse_filter = 2 -[node name="ClippingMask" type="TextureRect" parent="HBoxContainer/LayerName"] +[node name="ClippingMask" type="TextureRect" parent="LayerMainButton/LayerName"] unique_name_in_owner = true visible = false layout_mode = 2 texture = ExtResource("6_73j5q") stretch_mode = 5 -[node name="LayerNameLabel" type="Label" parent="HBoxContainer/LayerName"] +[node name="LayerNameLabel" type="Label" parent="LayerMainButton/LayerName"] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 text = "Layer 0" clip_text = true -[node name="LayerNameLineEdit" type="LineEdit" parent="HBoxContainer/LayerName"] +[node name="LayerNameLineEdit" type="LineEdit" parent="LayerMainButton/LayerName"] unique_name_in_owner = true visible = false layout_mode = 2 @@ -173,10 +161,6 @@ editable = false caret_blink = true caret_blink_interval = 0.5 -[node name="EmptySpacer" type="Control" parent="HBoxContainer/LayerName"] -layout_mode = 2 -mouse_filter = 2 - [node name="PopupMenu" type="PopupMenu" parent="."] disable_3d = true item_count = 1 @@ -184,10 +168,10 @@ item_0/text = "Clipping mask" item_0/checkable = 1 item_0/id = 0 -[connection signal="gui_input" from="." to="." method="_on_LayerContainer_gui_input"] -[connection signal="pressed" from="HBoxContainer/LayerButtons/ExpandButton" to="." method="_on_ExpandButton_pressed"] -[connection signal="pressed" from="HBoxContainer/LayerButtons/VisibilityButton" to="." method="_on_VisibilityButton_pressed"] -[connection signal="pressed" from="HBoxContainer/LayerButtons/LockButton" to="." method="_on_LockButton_pressed"] -[connection signal="pressed" from="HBoxContainer/LayerButtons/LinkButton" to="." method="_on_LinkButton_pressed"] -[connection signal="focus_exited" from="HBoxContainer/LayerName/LayerNameLineEdit" to="." method="_on_LineEdit_focus_exited"] +[connection signal="pressed" from="VisibilityButton" to="." method="_on_visibility_button_pressed"] +[connection signal="pressed" from="LockButton" to="." method="_on_lock_button_pressed"] +[connection signal="pressed" from="LinkButton" to="." method="_on_link_button_pressed"] +[connection signal="pressed" from="ExpandButton" to="." method="_on_expand_button_pressed"] +[connection signal="gui_input" from="LayerMainButton" to="." method="_on_main_button_gui_input"] +[connection signal="focus_exited" from="LayerMainButton/LayerName/LayerNameLineEdit" to="." method="_on_layer_name_line_edit_focus_exited"] [connection signal="id_pressed" from="PopupMenu" to="." method="_on_popup_menu_id_pressed"] diff --git a/src/UI/Timeline/LayerMainButton.gd b/src/UI/Timeline/LayerMainButton.gd new file mode 100644 index 000000000..f6acd6d5f --- /dev/null +++ b/src/UI/Timeline/LayerMainButton.gd @@ -0,0 +1,171 @@ +extends Button + +## The entire purpose of this script is to handle layer drag and dropping. + +var layer_index := 0 +var hierarchy_depth_pixel_shift := 16 + + +func _get_drag_data(_position: Vector2) -> Variant: + var layer := Global.current_project.layers[layer_index] + var layers := range(layer_index - layer.get_child_count(true), layer_index + 1) + var box := VBoxContainer.new() + for i in layers.size(): + var button := Button.new() + button.custom_minimum_size = size + button.theme = Global.control.theme + button.text = Global.current_project.layers[layers[-1 - i]].name + box.add_child(button) + set_drag_preview(box) + + return ["Layer", layer_index] + + +func _can_drop_data(_pos: Vector2, data) -> bool: + if typeof(data) != TYPE_ARRAY: + Global.animation_timeline.drag_highlight.visible = false + return false + if data[0] != "Layer": + Global.animation_timeline.drag_highlight.visible = false + return false + var curr_layer: BaseLayer = Global.current_project.layers[layer_index] + var drag_layer: BaseLayer = Global.current_project.layers[data[1]] + if curr_layer == drag_layer: + Global.animation_timeline.drag_highlight.visible = false + return false + + var region: Rect2 + var depth := curr_layer.get_hierarchy_depth() + if Input.is_action_pressed(&"ctrl"): # Swap layers + if drag_layer.is_ancestor_of(curr_layer) or curr_layer.is_ancestor_of(drag_layer): + Global.animation_timeline.drag_highlight.visible = false + return false + region = get_global_rect() + else: # Shift layers + if drag_layer.is_ancestor_of(curr_layer): + Global.animation_timeline.drag_highlight.visible = false + return false + # If accepted as a child, is it in the center region? + if ( + curr_layer.accepts_child(drag_layer) + and _get_region_rect(0.25, 0.75).has_point(get_global_mouse_position()) + ): + # Drawn regions are adjusted a bit from actual to clarify drop position + region = _get_region_rect(0.15, 0.85) + depth += 1 + else: + # Top or bottom region? + if _get_region_rect(0, 0.5).has_point(get_global_mouse_position()): + region = _get_region_rect(-0.1, 0.15) + else: + region = _get_region_rect(0.85, 1.1) + # Shift drawn region to the right a bit for hierarchy depth visualization: + region.position.x += depth * hierarchy_depth_pixel_shift + region.size.x -= depth * hierarchy_depth_pixel_shift + Global.animation_timeline.drag_highlight.global_position = region.position + Global.animation_timeline.drag_highlight.size = region.size + Global.animation_timeline.drag_highlight.visible = true + return true + + +func _drop_data(_pos: Vector2, data) -> void: + var drop_layer: int = data[1] + var project := Global.current_project + project.undo_redo.create_action("Change Layer Order") + var layers: Array = project.layers # This shouldn't be modified directly + var drop_from_indices := range( + drop_layer - layers[drop_layer].get_child_count(true), drop_layer + 1 + ) + var drop_from_parents := [] + for i in range(drop_from_indices.size()): + drop_from_parents.append(layers[drop_from_indices[i]].parent) + + if Input.is_action_pressed("ctrl"): # Swap layers + # a and b both need "from", "to", and "to_parents" + # a is this layer (and children), b is the dropped layers + var a := { + "from": range(layer_index - layers[layer_index].get_child_count(true), layer_index + 1) + } + var b := {"from": drop_from_indices} + + if a.from[0] < b.from[0]: + a["to"] = range(b.from[-1] + 1 - a.from.size(), b.from[-1] + 1) # Size of a, start from end of b + b["to"] = range(a.from[0], a.from[0] + b.from.size()) # Size of b, start from beginning of a + else: + a["to"] = range(b.from[0], b.from[0] + a.from.size()) # Size of a, start from beginning of b + b["to"] = range(a.from[-1] + 1 - b.from.size(), a.from[-1] + 1) # Size of b, start from end of a + + var a_from_parents := [] + for l in a.from: + a_from_parents.append(layers[l].parent) + + # to_parents starts as a dulpicate of from_parents, set the root layer's (with one layer or + # group with its children, this will always be the last layer [-1]) parent to the other + # root layer's parent + a["to_parents"] = a_from_parents.duplicate() + b["to_parents"] = drop_from_parents.duplicate() + a.to_parents[-1] = drop_from_parents[-1] + b.to_parents[-1] = a_from_parents[-1] + + project.undo_redo.add_do_method(project.swap_layers.bind(a, b)) + project.undo_redo.add_undo_method( + project.swap_layers.bind( + {"from": a.to, "to": a.from, "to_parents": a_from_parents}, + {"from": b.to, "to": drop_from_indices, "to_parents": drop_from_parents} + ) + ) + + else: # Move layers + var to_index: int # the index where the LOWEST moved layer should end up + var to_parent: BaseLayer + # If accepted as a child, is it in the center region? + if ( + layers[layer_index].accepts_child(layers[drop_layer]) + and _get_region_rect(0.25, 0.75).has_point(get_global_mouse_position()) + ): + to_index = layer_index + to_parent = layers[layer_index] + else: + # Top or bottom region? + if _get_region_rect(0, 0.5).has_point(get_global_mouse_position()): + to_index = layer_index + 1 + to_parent = layers[layer_index].parent + else: + # Place under the layer, if it has children, place after its lowest child + if layers[layer_index].has_children(): + to_index = layers[layer_index].get_children(true)[0].index + + if layers[layer_index].is_ancestor_of(layers[drop_layer]): + to_index += drop_from_indices.size() + else: + to_index = layer_index + to_parent = layers[layer_index].parent + + if drop_layer < layer_index: + to_index -= drop_from_indices.size() + + var drop_to_indices := range(to_index, to_index + drop_from_indices.size()) + var to_parents := drop_from_parents.duplicate() + to_parents[-1] = to_parent + + project.undo_redo.add_do_method( + project.move_layers.bind(drop_from_indices, drop_to_indices, to_parents) + ) + project.undo_redo.add_undo_method( + project.move_layers.bind(drop_to_indices, drop_from_indices, drop_from_parents) + ) + if project.current_layer == drop_layer: + project.undo_redo.add_do_method(project.change_cel.bind(-1, layer_index)) + else: + project.undo_redo.add_do_method(project.change_cel.bind(-1, project.current_layer)) + project.undo_redo.add_undo_method(project.change_cel.bind(-1, project.current_layer)) + project.undo_redo.add_undo_method(Global.undo_or_redo.bind(true)) + project.undo_redo.add_do_method(Global.undo_or_redo.bind(false)) + project.undo_redo.commit_action() + + +func _get_region_rect(y_begin: float, y_end: float) -> Rect2: + var rect := get_global_rect() + rect.position.y += rect.size.y * y_begin + rect.size.y *= y_end - y_begin + return rect