diff --git a/src/UI/Timeline/LayerButton.gd b/src/UI/Timeline/LayerButton.gd index 6c0b58579..0ccdaf7d7 100644 --- a/src/UI/Timeline/LayerButton.gd +++ b/src/UI/Timeline/LayerButton.gd @@ -110,38 +110,40 @@ func _input(event: InputEvent) -> void: _save_layer_name(line_edit.text) -func _on_main_button_gui_input(event: InputEvent) -> void: +func _on_layer_main_button_pressed() -> void: var project := Global.current_project + Global.canvas.selection.transform_content_confirm() + var prev_curr_layer := project.current_layer + if Input.is_action_pressed(&"shift"): + var layer_diff_sign := signi(layer_index - prev_curr_layer) + if layer_diff_sign == 0: + layer_diff_sign = 1 + for i in range(0, project.frames.size()): + for j in range(prev_curr_layer, layer_index + layer_diff_sign, layer_diff_sign): + var frame_layer := [i, j] + if !project.selected_cels.has(frame_layer): + project.selected_cels.append(frame_layer) + project.change_cel(-1, layer_index) + elif Input.is_action_pressed(&"ctrl"): + for i in range(0, project.frames.size()): + var frame_layer := [i, layer_index] + if !project.selected_cels.has(frame_layer): + project.selected_cels.append(frame_layer) + project.change_cel(-1, layer_index) + else: # If the button is pressed without Shift or Control + _select_current_layer() + +func _on_main_button_gui_input(event: InputEvent) -> void: if not event is InputEventMouseButton: return if event.button_index == MOUSE_BUTTON_LEFT: - Global.canvas.selection.transform_content_confirm() - var prev_curr_layer := project.current_layer - if Input.is_action_pressed(&"shift"): - var layer_diff_sign := signi(layer_index - prev_curr_layer) - if layer_diff_sign == 0: - layer_diff_sign = 1 - for i in range(0, project.frames.size()): - for j in range(prev_curr_layer, layer_index + layer_diff_sign, layer_diff_sign): - var frame_layer := [i, j] - if !project.selected_cels.has(frame_layer): - project.selected_cels.append(frame_layer) - project.change_cel(-1, layer_index) - elif Input.is_action_pressed(&"ctrl"): - for i in range(0, project.frames.size()): - var frame_layer := [i, layer_index] - if !project.selected_cels.has(frame_layer): - project.selected_cels.append(frame_layer) - project.change_cel(-1, layer_index) - else: # If the button is pressed without Shift or Control - _select_current_layer() - if event.double_click: label.visible = false line_edit.visible = true line_edit.editable = true line_edit.grab_focus() + elif event.button_index == MOUSE_BUTTON_RIGHT and event.pressed: var layer := Global.current_project.layers[layer_index] if not layer is GroupLayer: @@ -218,7 +220,7 @@ func _on_popup_menu_id_pressed(id: int) -> void: Global.canvas.draw_layers() -func _get_layer_indices() -> Array: +func _get_layer_indices() -> PackedInt32Array: var indices := [] for cel in Global.current_project.selected_cels: var l: int = cel[1] diff --git a/src/UI/Timeline/LayerButton.tscn b/src/UI/Timeline/LayerButton.tscn index 8353070cd..57e0881bf 100644 --- a/src/UI/Timeline/LayerButton.tscn +++ b/src/UI/Timeline/LayerButton.tscn @@ -123,7 +123,6 @@ focus_mode = 0 mouse_default_cursor_shape = 2 theme_type_variation = &"LayerFrameButton" toggle_mode = true -action_mode = 0 script = ExtResource("6_n8q6b") [node name="LayerName" type="HBoxContainer" parent="LayerMainButton"] @@ -180,5 +179,6 @@ item_1/id = 1 [connection signal="pressed" from="HBoxContainer/LinkButton" to="." method="_on_link_button_pressed"] [connection signal="pressed" from="HBoxContainer/ExpandButton" to="." method="_on_expand_button_pressed"] [connection signal="gui_input" from="LayerMainButton" to="." method="_on_main_button_gui_input"] +[connection signal="pressed" from="LayerMainButton" to="." method="_on_layer_main_button_pressed"] [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 index e3dbccfcc..deb60c7a3 100644 --- a/src/UI/Timeline/LayerMainButton.gd +++ b/src/UI/Timeline/LayerMainButton.gd @@ -7,8 +7,15 @@ 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 layers := _get_layer_indices() + for layer_i in layers: # Add child layers, if we have selected groups + var layer := Global.current_project.layers[layer_i] + for child in layer.get_children(true): + var child_index := Global.current_project.layers.find(child) + if not child_index in layers: # Do not add the same index multiple times + layers.append(child_index) + layers.sort() + var box := VBoxContainer.new() for i in layers.size(): var button := Button.new() @@ -17,47 +24,51 @@ func _get_drag_data(_position: Vector2) -> Variant: button.text = Global.current_project.layers[layers[-1 - i]].name box.add_child(button) set_drag_preview(box) - - return ["Layer", layer_index] + return ["Layer", layers] -func _can_drop_data(_pos: Vector2, data) -> bool: +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 curr_layer := Global.current_project.layers[layer_index] + var drop_layers: PackedInt32Array = data[1] + # Can't move to the same layer + for drop_layer in drop_layers: + if drop_layer == layer_index: + 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): + var last_layer := Global.current_project.layers[drop_layers[-1]] + if Input.is_action_pressed(&"ctrl") and drop_layers.size() == 1: # Swap layers + if last_layer.is_ancestor_of(curr_layer) or curr_layer.is_ancestor_of(last_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 + for drop_layer_index in drop_layers: + var drop_layer := Global.current_project.layers[drop_layer_index] + if drop_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()) + curr_layer.accepts_child(last_layer) # Any dropped layer should probably work here + and pos.y > size.y / 4.0 + and pos.y < 3.0 * size.y / 4.0 ): # 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()): + if pos.y < size.y / 2.0: # Top region region = _get_region_rect(-0.1, 0.15) - else: + else: # Bottom region 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 @@ -68,24 +79,39 @@ func _can_drop_data(_pos: Vector2, data) -> bool: return true -func _drop_data(_pos: Vector2, data) -> void: - var drop_layer: int = data[1] +func _drop_data(pos: Vector2, data) -> void: + var initial_drop_layers: PackedInt32Array = data[1] var project := Global.current_project - project.undo_redo.create_action("Change Layer Order") + var curr_layer := project.layers[layer_index] var layers := project.layers # This shouldn't be modified directly - var drop_from_indices: PackedInt32Array = range( - drop_layer - layers[drop_layer].get_child_count(true), drop_layer + 1 - ) + var drop_from_indices: PackedInt32Array = [] + var children_indices: PackedInt32Array = [] # Child layer indices, if a group layer is selected + # Add dropped indices to drop_from_indices + # We do this in case a child layer is selected along with its ancestor, + # we don't want both of them to be in the final array, as ancestors will automatically include + # their children anyway. + for drop_layer_index in initial_drop_layers: + if not drop_layer_index in drop_from_indices: # Do not add the same index multiple times + drop_from_indices.append(drop_layer_index) + var drop_layer := project.layers[drop_layer_index] + for child in drop_layer.get_children(true): + var child_index := project.layers.find(child) + if not child_index in children_indices: + children_indices.append(child_index) + if not child_index in drop_from_indices: # Do not add the same index multiple times + drop_from_indices.append(child_index) + drop_from_indices.sort() + children_indices.sort() + 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 + project.undo_redo.create_action("Change Layer Order") + if Input.is_action_pressed("ctrl") and initial_drop_layers.size() == 1: # 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 a := {"from": range(layer_index - curr_layer.get_child_count(true), layer_index + 1)} var b := {"from": drop_from_indices} if a.from[0] < b.from[0]: @@ -118,35 +144,40 @@ func _drop_data(_pos: Vector2, data) -> void: else: # Move layers var to_index: int # the index where the LOWEST moved layer should end up var to_parent: BaseLayer + var last_layer := project.layers[drop_from_indices[-1]] # 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()) + curr_layer.accepts_child(last_layer) # Any dropped layer should probably work here + and pos.y > size.y / 4.0 + and pos.y < 3.0 * size.y / 4.0 ): to_index = layer_index - to_parent = layers[layer_index] + to_parent = curr_layer else: - # Top or bottom region? - if _get_region_rect(0, 0.5).has_point(get_global_mouse_position()): + if pos.y < size.y / 2.0: # Top region to_index = layer_index + 1 - to_parent = layers[layer_index].parent - else: + to_parent = curr_layer.parent + else: # Bottom region # 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() + if curr_layer.has_children(): + to_index = curr_layer.get_children(true)[0].index + for drop_layer in drop_from_indices: + if curr_layer.is_ancestor_of(layers[drop_layer]): + to_index += 1 else: to_index = layer_index - to_parent = layers[layer_index].parent + to_parent = curr_layer.parent - if drop_layer < layer_index: - to_index -= drop_from_indices.size() + for drop_layer in drop_from_indices: + if drop_layer < layer_index: + to_index -= 1 var drop_to_indices: PackedInt32Array = range(to_index, to_index + drop_from_indices.size()) var to_parents := drop_from_parents.duplicate() - to_parents[-1] = to_parent + for i in to_parents.size(): + # Re-parent only the parent layers, not the child layers of a group + if not drop_from_indices[i] in children_indices: + to_parents[i] = to_parent project.undo_redo.add_do_method( project.move_layers.bind(drop_from_indices, drop_to_indices, to_parents) @@ -154,7 +185,7 @@ func _drop_data(_pos: Vector2, data) -> void: 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: + if project.current_layer in drop_from_indices: 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)) @@ -169,3 +200,15 @@ func _get_region_rect(y_begin: float, y_end: float) -> Rect2: rect.position.y += rect.size.y * y_begin rect.size.y *= y_end - y_begin return rect + + +func _get_layer_indices() -> PackedInt32Array: + 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 diff --git a/src/UI/Timeline/LayerProperties.gd b/src/UI/Timeline/LayerProperties.gd index 8fcd7767a..6453e1418 100644 --- a/src/UI/Timeline/LayerProperties.gd +++ b/src/UI/Timeline/LayerProperties.gd @@ -1,6 +1,6 @@ extends AcceptDialog -var layer_indices: Array +var layer_indices: PackedInt32Array @onready var name_line_edit := $GridContainer/NameLineEdit as LineEdit @onready var opacity_slider := $GridContainer/OpacitySlider as ValueSlider