mirror of
https://github.com/Orama-Interactive/Pixelorama.git
synced 2025-02-08 11:29:47 +00:00
8c9a01feae
The preferences code only handles the UI related stuff, while the Themes autoload is now solely responsible for theme adding, removing and changing. This makes it possible to handle themes without having the preferences dialog be in the middle.
336 lines
12 KiB
GDScript
336 lines
12 KiB
GDScript
extends Button
|
|
|
|
enum MenuOptions { PROPERTIES, DELETE, LINK, UNLINK }
|
|
|
|
var frame := 0
|
|
var layer := 0
|
|
var cel: BaseCel
|
|
|
|
var _is_guide_stylebox := false
|
|
|
|
@onready var popup_menu: PopupMenu = get_node_or_null("PopupMenu")
|
|
@onready var linked: ColorRect = $Linked
|
|
@onready var cel_texture: TextureRect = $CelTexture
|
|
@onready var transparent_checker: ColorRect = $CelTexture/TransparentChecker
|
|
@onready var properties: AcceptDialog = Global.control.find_child("CelProperties")
|
|
|
|
|
|
func _ready() -> void:
|
|
Global.cel_switched.connect(cel_switched)
|
|
Themes.theme_switched.connect(cel_switched.bind(true))
|
|
cel = Global.current_project.frames[frame].cels[layer]
|
|
button_setup()
|
|
_dim_checker()
|
|
cel.texture_changed.connect(_dim_checker)
|
|
for selected in Global.current_project.selected_cels:
|
|
if selected[1] == layer and selected[0] == frame:
|
|
button_pressed = true
|
|
if cel is GroupCel:
|
|
transparent_checker.visible = false
|
|
|
|
|
|
func cel_switched(force_stylebox_change := false) -> void:
|
|
z_index = 1 if button_pressed else 0
|
|
var current_theme := Global.control.theme
|
|
var is_guide := false
|
|
for selected in Global.current_project.selected_cels:
|
|
if selected[1] == layer or selected[0] == frame:
|
|
is_guide = true
|
|
break
|
|
if is_guide:
|
|
if not _is_guide_stylebox or force_stylebox_change:
|
|
var guide_stylebox := current_theme.get_stylebox("guide", "CelButton")
|
|
add_theme_stylebox_override("normal", guide_stylebox)
|
|
_is_guide_stylebox = true
|
|
else:
|
|
if _is_guide_stylebox or force_stylebox_change:
|
|
var normal_stylebox := current_theme.get_stylebox("normal", "CelButton")
|
|
add_theme_stylebox_override("normal", normal_stylebox)
|
|
_is_guide_stylebox = false
|
|
|
|
|
|
func button_setup() -> void:
|
|
custom_minimum_size.x = Global.animation_timeline.cel_size
|
|
custom_minimum_size.y = Global.animation_timeline.cel_size
|
|
|
|
var base_layer := Global.current_project.layers[layer]
|
|
tooltip_text = tr("Frame: %s, Layer: %s") % [frame + 1, base_layer.name]
|
|
cel_texture.texture = cel.image_texture
|
|
if is_instance_valid(linked):
|
|
linked.visible = cel.link_set != null
|
|
if cel.link_set != null:
|
|
linked.color.h = cel.link_set["hue"]
|
|
|
|
|
|
func _on_CelButton_pressed() -> void:
|
|
var project := Global.current_project
|
|
if Input.is_action_just_released("left_mouse"):
|
|
Global.canvas.selection.transform_content_confirm()
|
|
var change_cel := true
|
|
var prev_curr_frame: int = project.current_frame
|
|
var prev_curr_layer: int = project.current_layer
|
|
|
|
if Input.is_action_pressed("shift"):
|
|
var frame_diff_sign := signi(frame - prev_curr_frame)
|
|
if frame_diff_sign == 0:
|
|
frame_diff_sign = 1
|
|
var layer_diff_sign := signi(layer - prev_curr_layer)
|
|
if layer_diff_sign == 0:
|
|
layer_diff_sign = 1
|
|
for i in range(prev_curr_frame, frame + frame_diff_sign, frame_diff_sign):
|
|
for j in range(prev_curr_layer, layer + layer_diff_sign, layer_diff_sign):
|
|
var frame_layer := [i, j]
|
|
if !project.selected_cels.has(frame_layer):
|
|
project.selected_cels.append(frame_layer)
|
|
elif Input.is_action_pressed("ctrl"):
|
|
var frame_layer := [frame, layer]
|
|
if project.selected_cels.has(frame_layer):
|
|
if project.selected_cels.size() > 1:
|
|
project.selected_cels.erase(frame_layer)
|
|
change_cel = false
|
|
else:
|
|
project.selected_cels.append(frame_layer)
|
|
else: # If the button is pressed without Shift or Control
|
|
project.selected_cels.clear()
|
|
var frame_layer := [frame, layer]
|
|
if !project.selected_cels.has(frame_layer):
|
|
project.selected_cels.append(frame_layer)
|
|
|
|
if change_cel:
|
|
project.change_cel(frame, layer)
|
|
else:
|
|
project.change_cel(project.selected_cels[0][0], project.selected_cels[0][1])
|
|
release_focus()
|
|
|
|
elif Input.is_action_just_released("right_mouse"):
|
|
if cel is PixelCel:
|
|
popup_menu.popup(Rect2(get_global_mouse_position(), Vector2.ONE))
|
|
button_pressed = !button_pressed
|
|
elif Input.is_action_just_released("middle_mouse"):
|
|
button_pressed = !button_pressed
|
|
_delete_cel_content()
|
|
else: # An example of this would be Space
|
|
button_pressed = !button_pressed
|
|
|
|
|
|
func _on_PopupMenu_id_pressed(id: int) -> void:
|
|
match id:
|
|
MenuOptions.PROPERTIES:
|
|
properties.cel_indices = _get_cel_indices()
|
|
properties.popup_centered()
|
|
MenuOptions.DELETE:
|
|
_delete_cel_content()
|
|
|
|
MenuOptions.LINK, MenuOptions.UNLINK:
|
|
var project := Global.current_project
|
|
if id == MenuOptions.UNLINK:
|
|
project.undo_redo.create_action("Unlink Cel")
|
|
var selected_cels := _get_cel_indices(true)
|
|
if not selected_cels.has([frame, layer]):
|
|
selected_cels.append([frame, layer]) # Include this cel with the selected ones
|
|
for cel_index in selected_cels:
|
|
if layer != cel_index[1]: # Skip selected cels not on the same layer
|
|
continue
|
|
var s_cel := project.frames[cel_index[0]].cels[cel_index[1]]
|
|
if s_cel.link_set == null: # Skip cels that aren't linked
|
|
continue
|
|
project.undo_redo.add_do_method(
|
|
project.layers[layer].link_cel.bind(s_cel, null)
|
|
)
|
|
project.undo_redo.add_undo_method(
|
|
project.layers[layer].link_cel.bind(s_cel, s_cel.link_set)
|
|
)
|
|
if s_cel.link_set.size() > 1: # Skip copying content if not linked to another
|
|
project.undo_redo.add_do_method(
|
|
s_cel.set_content.bind(s_cel.copy_content(), ImageTexture.new())
|
|
)
|
|
project.undo_redo.add_undo_method(
|
|
s_cel.set_content.bind(s_cel.get_content(), s_cel.image_texture)
|
|
)
|
|
|
|
elif id == MenuOptions.LINK:
|
|
project.undo_redo.create_action("Link Cel")
|
|
var link_set: Dictionary = {} if cel.link_set == null else cel.link_set
|
|
if cel.link_set == null:
|
|
project.undo_redo.add_do_method(
|
|
project.layers[layer].link_cel.bind(cel, link_set)
|
|
)
|
|
project.undo_redo.add_undo_method(
|
|
project.layers[layer].link_cel.bind(cel, null)
|
|
)
|
|
|
|
for cel_index in project.selected_cels:
|
|
if layer != cel_index[1]: # Skip selected cels not on the same layer
|
|
continue
|
|
var s_cel := project.frames[cel_index[0]].cels[cel_index[1]]
|
|
if cel == s_cel: # Don't need to link cel to itself
|
|
continue
|
|
if s_cel.link_set == link_set: # Skip cels that were already linked
|
|
continue
|
|
project.undo_redo.add_do_method(
|
|
project.layers[layer].link_cel.bind(s_cel, link_set)
|
|
)
|
|
project.undo_redo.add_undo_method(
|
|
project.layers[layer].link_cel.bind(s_cel, s_cel.link_set)
|
|
)
|
|
project.undo_redo.add_do_method(
|
|
s_cel.set_content.bind(cel.get_content(), cel.image_texture)
|
|
)
|
|
project.undo_redo.add_undo_method(
|
|
s_cel.set_content.bind(s_cel.get_content(), s_cel.image_texture)
|
|
)
|
|
|
|
# Remove and add a new cel button to update appearance (can't use button_setup
|
|
# because there is no guarantee that it will be the exact same cel button instance)
|
|
# May be able to use button_setup with a lambda to find correct cel button in Godot 4
|
|
for f in project.frames.size():
|
|
project.undo_redo.add_do_method(
|
|
Global.animation_timeline.project_cel_removed.bind(f, layer)
|
|
)
|
|
project.undo_redo.add_undo_method(
|
|
Global.animation_timeline.project_cel_removed.bind(f, layer)
|
|
)
|
|
project.undo_redo.add_do_method(
|
|
Global.animation_timeline.project_cel_added.bind(f, layer)
|
|
)
|
|
project.undo_redo.add_undo_method(
|
|
Global.animation_timeline.project_cel_added.bind(f, layer)
|
|
)
|
|
|
|
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()
|
|
|
|
|
|
func _delete_cel_content() -> void:
|
|
var indices := _get_cel_indices()
|
|
var project := Global.current_project
|
|
project.undos += 1
|
|
project.undo_redo.create_action("Draw")
|
|
for cel_index in indices:
|
|
var frame_index: int = cel_index[0]
|
|
var layer_index: int = cel_index[1]
|
|
var selected_cel := project.frames[frame_index].cels[layer_index]
|
|
var empty_content = selected_cel.create_empty_content()
|
|
var old_content = selected_cel.get_content()
|
|
if selected_cel.link_set == null:
|
|
project.undo_redo.add_do_method(selected_cel.set_content.bind(empty_content))
|
|
project.undo_redo.add_undo_method(selected_cel.set_content.bind(old_content))
|
|
else:
|
|
for linked_cel in selected_cel.link_set["cels"]:
|
|
project.undo_redo.add_do_method(linked_cel.set_content.bind(empty_content))
|
|
project.undo_redo.add_undo_method(linked_cel.set_content.bind(old_content))
|
|
project.undo_redo.add_do_method(
|
|
Global.undo_or_redo.bind(false, frame_index, layer_index, project)
|
|
)
|
|
project.undo_redo.add_undo_method(
|
|
Global.undo_or_redo.bind(true, frame_index, layer_index, project)
|
|
)
|
|
project.undo_redo.commit_action()
|
|
|
|
|
|
func _dim_checker() -> void:
|
|
var image := cel.get_image()
|
|
if image == null:
|
|
return
|
|
if image.is_empty() or image.is_invisible():
|
|
transparent_checker.visible = false
|
|
else:
|
|
transparent_checker.visible = true
|
|
|
|
|
|
func _get_drag_data(_position: Vector2) -> Variant:
|
|
var button := Button.new()
|
|
button.size = size
|
|
button.theme = Global.control.theme
|
|
var texture_rect := TextureRect.new()
|
|
texture_rect.size = cel_texture.size
|
|
texture_rect.position = cel_texture.position
|
|
texture_rect.expand = true
|
|
texture_rect.texture = cel_texture.texture
|
|
button.add_child(texture_rect)
|
|
set_drag_preview(button)
|
|
|
|
return ["Cel", frame, layer]
|
|
|
|
|
|
func _can_drop_data(_pos: Vector2, data) -> bool:
|
|
var project := Global.current_project
|
|
if typeof(data) == TYPE_ARRAY and data[0] == "Cel":
|
|
var drag_frame = data[1]
|
|
var drag_layer = data[2]
|
|
if project.layers[drag_layer].get_script() == project.layers[layer].get_script():
|
|
if ( # If both cels are on the same layer, or both are not linked
|
|
drag_layer == layer
|
|
or (
|
|
project.frames[frame].cels[layer].link_set == null
|
|
and project.frames[drag_frame].cels[drag_layer].link_set == null
|
|
)
|
|
):
|
|
if not (drag_frame == frame and drag_layer == layer):
|
|
var region: Rect2
|
|
if Input.is_action_pressed("ctrl") or layer != drag_layer: # Swap cels
|
|
region = get_global_rect()
|
|
else: # Move cels
|
|
if _get_region_rect(0, 0.5).has_point(get_global_mouse_position()): # Left
|
|
region = _get_region_rect(-0.125, 0.125)
|
|
region.position.x -= 2 # Container spacing
|
|
else: # Right
|
|
region = _get_region_rect(0.875, 1.125)
|
|
region.position.x += 2 # Container spacing
|
|
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
|
|
|
|
Global.animation_timeline.drag_highlight.visible = false
|
|
return false
|
|
|
|
|
|
func _drop_data(_pos: Vector2, data) -> void:
|
|
var drop_frame: int = data[1]
|
|
var drop_layer: int = data[2]
|
|
var project := Global.current_project
|
|
|
|
project.undo_redo.create_action("Move Cels")
|
|
if Input.is_action_pressed("ctrl") or layer != drop_layer: # Swap cels
|
|
project.undo_redo.add_do_method(project.swap_cel.bind(frame, layer, drop_frame, drop_layer))
|
|
project.undo_redo.add_undo_method(
|
|
project.swap_cel.bind(frame, layer, drop_frame, drop_layer)
|
|
)
|
|
else: # Move cels
|
|
var to_frame: int
|
|
if _get_region_rect(0, 0.5).has_point(get_global_mouse_position()): # Left
|
|
to_frame = frame
|
|
else: # Right
|
|
to_frame = frame + 1
|
|
if drop_frame < frame:
|
|
to_frame -= 1
|
|
project.undo_redo.add_do_method(project.move_cel.bind(drop_frame, to_frame, layer))
|
|
project.undo_redo.add_undo_method(project.move_cel.bind(to_frame, drop_frame, layer))
|
|
|
|
project.undo_redo.add_do_method(project.change_cel.bind(frame, layer))
|
|
project.undo_redo.add_undo_method(
|
|
project.change_cel.bind(project.current_frame, 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(x_begin: float, x_end: float) -> Rect2:
|
|
var rect := get_global_rect()
|
|
rect.position.x += rect.size.x * x_begin
|
|
rect.size.x *= x_end - x_begin
|
|
return rect
|
|
|
|
|
|
func _get_cel_indices(add_current_cel := false) -> Array:
|
|
var indices := Global.current_project.selected_cels.duplicate()
|
|
if not [frame, layer] in indices:
|
|
if add_current_cel:
|
|
indices.append([frame, layer])
|
|
else:
|
|
indices = [[frame, layer]]
|
|
return indices
|