mirror of
https://github.com/tonytins/CozyPixelStudio.git
synced 2025-06-25 07:14:42 -04:00
Merged "Scripts" and "Prefabs" folders into "src"
Made a new "src" folder that will contain the source code files, like all the GDScript and scene files. Please read this for more details: https://www.gdquest.com/docs/guidelines/best-practices/godot-gdscript/ It made no sense to keep scenes separate from their scripts. More file organizing will follow soon.
This commit is contained in:
parent
5a54235604
commit
646fc19a70
63 changed files with 135 additions and 113 deletions
24
src/AnimationTag.tscn
Normal file
24
src/AnimationTag.tscn
Normal file
|
@ -0,0 +1,24 @@
|
|||
[gd_scene format=2]
|
||||
|
||||
[node name="AnimationTag" type="VBoxContainer"]
|
||||
margin_right = 39.0
|
||||
margin_bottom = 32.0
|
||||
rect_min_size = Vector2( 39, 32 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Line2D" type="Line2D" parent="."]
|
||||
points = PoolVector2Array( 0, 32, 0, 0, 39, 0, 39, 32 )
|
||||
width = 1.0
|
||||
texture_mode = 1313163520
|
||||
|
||||
[node name="Label" type="Label" parent="."]
|
||||
margin_left = 7.0
|
||||
margin_right = 32.0
|
||||
margin_bottom = 32.0
|
||||
size_flags_horizontal = 4
|
||||
size_flags_vertical = 3
|
||||
text = "Idle"
|
||||
align = 1
|
||||
valign = 1
|
494
src/AnimationTimeline.gd
Normal file
494
src/AnimationTimeline.gd
Normal file
|
@ -0,0 +1,494 @@
|
|||
extends Panel
|
||||
|
||||
var fps := 6.0
|
||||
var animation_loop := 1 # 0 is no loop, 1 is cycle loop, 2 is ping-pong loop
|
||||
var animation_forward := true
|
||||
var first_frame := 0
|
||||
var last_frame := Global.canvases.size() - 1
|
||||
|
||||
onready var timeline_scroll : ScrollContainer = $AnimationContainer/TimelineContainer/TimelineScroll
|
||||
onready var tag_scroll_container : ScrollContainer = $AnimationContainer/TimelineContainer/OpacityAndTagContainer/TagScroll
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
timeline_scroll.get_h_scrollbar().connect("value_changed", self, "_h_scroll_changed")
|
||||
Global.animation_timer.wait_time = 1 / fps
|
||||
|
||||
|
||||
func _h_scroll_changed(value : float) -> void:
|
||||
# Let the main timeline ScrollContainer affect the tag ScrollContainer too
|
||||
tag_scroll_container.get_child(0).rect_min_size.x = timeline_scroll.get_child(0).rect_size.x - 212
|
||||
tag_scroll_container.scroll_horizontal = value
|
||||
|
||||
|
||||
func add_frame() -> void:
|
||||
var new_canvas : Canvas = load("res://src/Canvas.tscn").instance()
|
||||
new_canvas.size = Global.canvas.size
|
||||
new_canvas.frame = Global.canvases.size()
|
||||
|
||||
var new_canvases: Array = Global.canvases.duplicate()
|
||||
new_canvases.append(new_canvas)
|
||||
|
||||
Global.undos += 1
|
||||
Global.undo_redo.create_action("Add Frame")
|
||||
Global.undo_redo.add_do_method(Global, "redo", [new_canvas])
|
||||
Global.undo_redo.add_undo_method(Global, "undo", [new_canvas])
|
||||
|
||||
Global.undo_redo.add_do_property(Global, "canvases", new_canvases)
|
||||
Global.undo_redo.add_do_property(Global, "canvas", new_canvas)
|
||||
Global.undo_redo.add_do_property(Global, "current_frame", new_canvases.size() - 1)
|
||||
|
||||
for c in Global.canvases:
|
||||
Global.undo_redo.add_do_property(c, "visible", false)
|
||||
Global.undo_redo.add_undo_property(c, "visible", c.visible)
|
||||
|
||||
for l_i in range(Global.layers.size()):
|
||||
if Global.layers[l_i][4]: # If the link button is pressed
|
||||
Global.layers[l_i][5].append(new_canvas)
|
||||
|
||||
Global.undo_redo.add_undo_property(Global, "canvases", Global.canvases)
|
||||
Global.undo_redo.add_undo_property(Global, "canvas", Global.canvas)
|
||||
Global.undo_redo.add_undo_property(Global, "current_frame", Global.current_frame)
|
||||
Global.undo_redo.commit_action()
|
||||
|
||||
|
||||
func _on_DeleteFrame_pressed(frame := -1) -> void:
|
||||
if Global.canvases.size() == 1:
|
||||
return
|
||||
if frame == -1:
|
||||
frame = Global.current_frame
|
||||
|
||||
var canvas : Canvas = Global.canvases[frame]
|
||||
var new_canvases := Global.canvases.duplicate()
|
||||
new_canvases.erase(canvas)
|
||||
var current_frame := Global.current_frame
|
||||
if current_frame > 0 && current_frame == new_canvases.size(): # If it's the last frame
|
||||
current_frame -= 1
|
||||
|
||||
var new_animation_tags := Global.animation_tags.duplicate(true)
|
||||
# Loop through the tags to see if the frame is in one
|
||||
for tag in new_animation_tags:
|
||||
if frame + 1 >= tag[2] && frame + 1 <= tag[3]:
|
||||
if tag[3] == tag[2]: # If we're deleting the only frame in the tag
|
||||
new_animation_tags.erase(tag)
|
||||
else:
|
||||
tag[3] -= 1
|
||||
elif frame + 1 < tag[2]:
|
||||
tag[2] -= 1
|
||||
tag[3] -= 1
|
||||
|
||||
# Check if one of the cels of the frame is linked
|
||||
# if they are, unlink them too
|
||||
# this prevents removed cels being kept in linked memory
|
||||
var new_layers := Global.layers.duplicate(true)
|
||||
for layer in new_layers:
|
||||
for linked in layer[5]:
|
||||
if linked == Global.canvases[frame]:
|
||||
layer[5].erase(linked)
|
||||
|
||||
Global.undos += 1
|
||||
Global.undo_redo.create_action("Remove Frame")
|
||||
|
||||
Global.undo_redo.add_do_property(Global, "canvases", new_canvases)
|
||||
Global.undo_redo.add_do_property(Global, "canvas", new_canvases[current_frame])
|
||||
Global.undo_redo.add_do_property(Global, "current_frame", current_frame)
|
||||
Global.undo_redo.add_do_property(Global, "animation_tags", new_animation_tags)
|
||||
Global.undo_redo.add_do_property(Global, "layers", new_layers)
|
||||
|
||||
# Change the frame value of the canvaseso on the right
|
||||
# for example, if frame "3" was deleted, then "4" would have to become "3"
|
||||
for i in range(frame, new_canvases.size()):
|
||||
var c : Canvas = new_canvases[i]
|
||||
Global.undo_redo.add_do_property(c, "frame", i)
|
||||
Global.undo_redo.add_undo_property(c, "frame", c.frame)
|
||||
|
||||
|
||||
Global.undo_redo.add_undo_property(Global, "canvases", Global.canvases)
|
||||
Global.undo_redo.add_undo_property(Global, "canvas", canvas)
|
||||
Global.undo_redo.add_undo_property(Global, "current_frame", Global.current_frame)
|
||||
Global.undo_redo.add_undo_property(Global, "animation_tags", Global.animation_tags)
|
||||
Global.undo_redo.add_undo_property(Global, "layers", Global.layers)
|
||||
|
||||
Global.undo_redo.add_do_method(Global, "redo", [canvas])
|
||||
Global.undo_redo.add_undo_method(Global, "undo", [canvas])
|
||||
Global.undo_redo.commit_action()
|
||||
|
||||
|
||||
func _on_CopyFrame_pressed(frame := -1) -> void:
|
||||
if frame == -1:
|
||||
frame = Global.current_frame
|
||||
|
||||
var canvas : Canvas = Global.canvases[frame]
|
||||
var new_canvas : Canvas = load("res://src/Canvas.tscn").instance()
|
||||
new_canvas.size = Global.canvas.size
|
||||
new_canvas.frame = Global.canvases.size()
|
||||
|
||||
var new_canvases := Global.canvases.duplicate()
|
||||
new_canvases.insert(frame + 1, new_canvas)
|
||||
|
||||
for layer in canvas.layers: # Copy every layer
|
||||
var sprite := Image.new()
|
||||
sprite.copy_from(layer[0])
|
||||
sprite.lock()
|
||||
var tex := ImageTexture.new()
|
||||
tex.create_from_image(sprite, 0)
|
||||
new_canvas.layers.append([sprite, tex, layer[2]])
|
||||
|
||||
var new_animation_tags := Global.animation_tags.duplicate(true)
|
||||
# Loop through the tags to see if the frame is in one
|
||||
for tag in new_animation_tags:
|
||||
if frame + 1 >= tag[2] && frame + 1 <= tag[3]:
|
||||
tag[3] += 1
|
||||
|
||||
Global.undos += 1
|
||||
Global.undo_redo.create_action("Add Frame")
|
||||
Global.undo_redo.add_do_method(Global, "redo", [new_canvas])
|
||||
Global.undo_redo.add_undo_method(Global, "undo", [new_canvas])
|
||||
|
||||
Global.undo_redo.add_do_property(Global, "canvases", new_canvases)
|
||||
Global.undo_redo.add_do_property(Global, "canvas", new_canvas)
|
||||
Global.undo_redo.add_do_property(Global, "current_frame", frame + 1)
|
||||
Global.undo_redo.add_do_property(Global, "animation_tags", new_animation_tags)
|
||||
for i in range(Global.layers.size()):
|
||||
for child in Global.layers[i][3].get_children():
|
||||
Global.undo_redo.add_do_property(child, "pressed", false)
|
||||
Global.undo_redo.add_undo_property(child, "pressed", child.pressed)
|
||||
for c in Global.canvases:
|
||||
Global.undo_redo.add_do_property(c, "visible", false)
|
||||
Global.undo_redo.add_undo_property(c, "visible", c.visible)
|
||||
|
||||
for i in range(frame, new_canvases.size()):
|
||||
var c : Canvas = new_canvases[i]
|
||||
Global.undo_redo.add_do_property(c, "frame", i)
|
||||
Global.undo_redo.add_undo_property(c, "frame", c.frame)
|
||||
|
||||
Global.undo_redo.add_undo_property(Global, "canvases", Global.canvases)
|
||||
Global.undo_redo.add_undo_property(Global, "canvas", Global.canvas)
|
||||
Global.undo_redo.add_undo_property(Global, "current_frame", frame)
|
||||
Global.undo_redo.add_undo_property(Global, "animation_tags", Global.animation_tags)
|
||||
Global.undo_redo.commit_action()
|
||||
|
||||
|
||||
func _on_FrameTagButton_pressed() -> void:
|
||||
Global.tag_dialog.popup_centered()
|
||||
|
||||
|
||||
func _on_OnionSkinning_pressed() -> void:
|
||||
Global.onion_skinning = !Global.onion_skinning
|
||||
Global.canvas.update()
|
||||
var theme_type := Global.theme_type
|
||||
if theme_type == "Gold":
|
||||
theme_type = "Light"
|
||||
var texture_button : TextureRect = Global.onion_skinning_button.get_child(0)
|
||||
if Global.onion_skinning:
|
||||
texture_button.texture = load("res://Assets/Graphics/%s Themes/Timeline/onion_skinning.png" % theme_type)
|
||||
else:
|
||||
texture_button.texture = load("res://Assets/Graphics/%s Themes/Timeline/onion_skinning_off.png" % theme_type)
|
||||
|
||||
|
||||
func _on_OnionSkinningSettings_pressed() -> void:
|
||||
$OnionSkinningSettings.popup(Rect2(Global.onion_skinning_button.rect_global_position.x - $OnionSkinningSettings.rect_size.x - 16, Global.onion_skinning_button.rect_global_position.y - 106, 136, 126))
|
||||
|
||||
|
||||
func _on_LoopAnim_pressed() -> void:
|
||||
var texture_button : TextureRect = Global.loop_animation_button.get_child(0)
|
||||
var theme_type := Global.theme_type
|
||||
if theme_type == "Gold":
|
||||
theme_type = "Light"
|
||||
match animation_loop:
|
||||
0: # Make it loop
|
||||
animation_loop = 1
|
||||
texture_button.texture = load("res://Assets/Graphics/%s Themes/Timeline/loop.png" % theme_type)
|
||||
Global.loop_animation_button.hint_tooltip = "Cycle loop"
|
||||
1: # Make it ping-pong
|
||||
animation_loop = 2
|
||||
texture_button.texture = load("res://Assets/Graphics/%s Themes/Timeline/loop_pingpong.png" % theme_type)
|
||||
Global.loop_animation_button.hint_tooltip = "Ping-pong loop"
|
||||
2: # Make it stop
|
||||
animation_loop = 0
|
||||
texture_button.texture = load("res://Assets/Graphics/%s Themes/Timeline/loop_none.png" % theme_type)
|
||||
Global.loop_animation_button.hint_tooltip = "No loop"
|
||||
|
||||
|
||||
func _on_PlayForward_toggled(button_pressed : bool) -> void:
|
||||
var theme_type := Global.theme_type
|
||||
if theme_type == "Gold":
|
||||
theme_type = "Light"
|
||||
if button_pressed:
|
||||
Global.play_forward.get_child(0).texture = load("res://Assets/Graphics/%s Themes/Timeline/pause.png" % theme_type)
|
||||
else:
|
||||
Global.play_forward.get_child(0).texture = load("res://Assets/Graphics/%s Themes/Timeline/play.png" % theme_type)
|
||||
|
||||
play_animation(button_pressed, true)
|
||||
|
||||
|
||||
func _on_PlayBackwards_toggled(button_pressed : bool) -> void:
|
||||
var theme_type := Global.theme_type
|
||||
if theme_type == "Gold":
|
||||
theme_type = "Light"
|
||||
if button_pressed:
|
||||
Global.play_backwards.get_child(0).texture = load("res://Assets/Graphics/%s Themes/Timeline/pause.png" % theme_type)
|
||||
else:
|
||||
Global.play_backwards.get_child(0).texture = load("res://Assets/Graphics/%s Themes/Timeline/play_backwards.png" % theme_type)
|
||||
|
||||
play_animation(button_pressed, false)
|
||||
|
||||
|
||||
func _on_AnimationTimer_timeout() -> void:
|
||||
if animation_forward:
|
||||
if Global.current_frame < last_frame:
|
||||
Global.current_frame += 1
|
||||
else:
|
||||
match animation_loop:
|
||||
0: # No loop
|
||||
Global.play_forward.pressed = false
|
||||
Global.play_backwards.pressed = false
|
||||
Global.animation_timer.stop()
|
||||
1: # Cycle loop
|
||||
Global.current_frame = first_frame
|
||||
2: # Ping pong loop
|
||||
animation_forward = false
|
||||
_on_AnimationTimer_timeout()
|
||||
|
||||
else:
|
||||
if Global.current_frame > first_frame:
|
||||
Global.current_frame -= 1
|
||||
else:
|
||||
match animation_loop:
|
||||
0: # No loop
|
||||
Global.play_backwards.pressed = false
|
||||
Global.play_forward.pressed = false
|
||||
Global.animation_timer.stop()
|
||||
1: # Cycle loop
|
||||
Global.current_frame = last_frame
|
||||
2: # Ping pong loop
|
||||
animation_forward = true
|
||||
_on_AnimationTimer_timeout()
|
||||
|
||||
|
||||
func play_animation(play : bool, forward_dir : bool) -> void:
|
||||
var theme_type := Global.theme_type
|
||||
if theme_type == "Gold":
|
||||
theme_type = "Light"
|
||||
|
||||
if forward_dir:
|
||||
Global.play_backwards.disconnect("toggled", self, "_on_PlayBackwards_toggled")
|
||||
Global.play_backwards.pressed = false
|
||||
Global.play_backwards.get_child(0).texture = load("res://Assets/Graphics/%s Themes/Timeline/play_backwards.png" % theme_type)
|
||||
Global.play_backwards.connect("toggled", self, "_on_PlayBackwards_toggled")
|
||||
else:
|
||||
Global.play_forward.disconnect("toggled", self, "_on_PlayForward_toggled")
|
||||
Global.play_forward.pressed = false
|
||||
Global.play_forward.get_child(0).texture = load("res://Assets/Graphics/%s Themes/Timeline/play.png" % theme_type)
|
||||
Global.play_forward.connect("toggled", self, "_on_PlayForward_toggled")
|
||||
if Global.canvases.size() == 1:
|
||||
if forward_dir:
|
||||
Global.play_forward.pressed = false
|
||||
else:
|
||||
Global.play_backwards.pressed = false
|
||||
return
|
||||
|
||||
first_frame = 0
|
||||
last_frame = Global.canvases.size() - 1
|
||||
if Global.play_only_tags:
|
||||
for tag in Global.animation_tags:
|
||||
if Global.current_frame + 1 >= tag[2] && Global.current_frame + 1 <= tag[3]:
|
||||
first_frame = tag[2] - 1
|
||||
last_frame = min(Global.canvases.size() - 1, tag[3] - 1)
|
||||
|
||||
if play:
|
||||
Global.animation_timer.wait_time = 1 / fps
|
||||
Global.animation_timer.start()
|
||||
animation_forward = forward_dir
|
||||
else:
|
||||
Global.animation_timer.stop()
|
||||
|
||||
|
||||
func _on_NextFrame_pressed() -> void:
|
||||
if Global.current_frame < Global.canvases.size() - 1:
|
||||
Global.current_frame += 1
|
||||
|
||||
|
||||
func _on_PreviousFrame_pressed() -> void:
|
||||
if Global.current_frame > 0:
|
||||
Global.current_frame -= 1
|
||||
|
||||
|
||||
func _on_LastFrame_pressed() -> void:
|
||||
Global.current_frame = Global.canvases.size() - 1
|
||||
|
||||
|
||||
func _on_FirstFrame_pressed() -> void:
|
||||
Global.current_frame = 0
|
||||
|
||||
|
||||
func _on_FPSValue_value_changed(value : float) -> void:
|
||||
fps = float(value)
|
||||
Global.animation_timer.wait_time = 1 / fps
|
||||
|
||||
|
||||
func _on_PastOnionSkinning_value_changed(value : float) -> void:
|
||||
Global.onion_skinning_past_rate = int(value)
|
||||
Global.canvas.update()
|
||||
|
||||
|
||||
func _on_FutureOnionSkinning_value_changed(value : float) -> void:
|
||||
Global.onion_skinning_future_rate = int(value)
|
||||
Global.canvas.update()
|
||||
|
||||
|
||||
func _on_BlueRedMode_toggled(button_pressed : bool) -> void:
|
||||
Global.onion_skinning_blue_red = button_pressed
|
||||
Global.canvas.update()
|
||||
|
||||
|
||||
# Layer buttons
|
||||
|
||||
func add_layer(is_new := true) -> void:
|
||||
var layer_name = null
|
||||
if !is_new: # Clone layer
|
||||
layer_name = Global.layers[Global.current_layer][0] + " (" + tr("copy") + ")"
|
||||
|
||||
var new_layers : Array = Global.layers.duplicate()
|
||||
|
||||
# Store [Layer name (0), Layer visibility boolean (1), Layer lock boolean (2), Frame container (3),
|
||||
# will new frames be linked boolean (4), Array of linked frames (5)]
|
||||
new_layers.append([layer_name, true, false, HBoxContainer.new(), false, []])
|
||||
|
||||
Global.undos += 1
|
||||
Global.undo_redo.create_action("Add Layer")
|
||||
|
||||
for c in Global.canvases:
|
||||
var new_layer := Image.new()
|
||||
if is_new:
|
||||
new_layer.create(c.size.x, c.size.y, false, Image.FORMAT_RGBA8)
|
||||
else: # Clone layer
|
||||
new_layer.copy_from(c.layers[Global.current_layer][0])
|
||||
|
||||
new_layer.lock()
|
||||
var new_layer_tex := ImageTexture.new()
|
||||
new_layer_tex.create_from_image(new_layer, 0)
|
||||
|
||||
var new_canvas_layers : Array = c.layers.duplicate()
|
||||
# Store [Image, ImageTexture, Opacity]
|
||||
new_canvas_layers.append([new_layer, new_layer_tex, 1])
|
||||
Global.undo_redo.add_do_property(c, "layers", new_canvas_layers)
|
||||
Global.undo_redo.add_undo_property(c, "layers", c.layers)
|
||||
|
||||
Global.undo_redo.add_do_property(Global, "current_layer", Global.current_layer + 1)
|
||||
Global.undo_redo.add_do_property(Global, "layers", new_layers)
|
||||
Global.undo_redo.add_undo_property(Global, "current_layer", Global.current_layer)
|
||||
Global.undo_redo.add_undo_property(Global, "layers", Global.layers)
|
||||
|
||||
Global.undo_redo.add_undo_method(Global, "undo", [Global.canvas])
|
||||
Global.undo_redo.add_do_method(Global, "redo", [Global.canvas])
|
||||
Global.undo_redo.commit_action()
|
||||
|
||||
|
||||
func _on_RemoveLayer_pressed() -> void:
|
||||
var new_layers : Array = Global.layers.duplicate()
|
||||
new_layers.remove(Global.current_layer)
|
||||
Global.undos += 1
|
||||
Global.undo_redo.create_action("Remove Layer")
|
||||
if Global.current_layer > 0:
|
||||
Global.undo_redo.add_do_property(Global, "current_layer", Global.current_layer - 1)
|
||||
else:
|
||||
Global.undo_redo.add_do_property(Global, "current_layer", Global.current_layer)
|
||||
|
||||
for c in Global.canvases:
|
||||
var new_canvas_layers : Array = c.layers.duplicate()
|
||||
new_canvas_layers.remove(Global.current_layer)
|
||||
Global.undo_redo.add_do_property(c, "layers", new_canvas_layers)
|
||||
Global.undo_redo.add_undo_property(c, "layers", c.layers)
|
||||
|
||||
Global.undo_redo.add_do_property(Global, "layers", new_layers)
|
||||
Global.undo_redo.add_undo_property(Global, "current_layer", Global.current_layer)
|
||||
Global.undo_redo.add_undo_property(Global, "layers", Global.layers)
|
||||
Global.undo_redo.add_do_method(Global, "redo", [Global.canvas])
|
||||
Global.undo_redo.add_undo_method(Global, "undo", [Global.canvas])
|
||||
Global.undo_redo.commit_action()
|
||||
|
||||
|
||||
func change_layer_order(rate : int) -> void:
|
||||
var change = Global.current_layer + rate
|
||||
|
||||
var new_layers : Array = Global.layers.duplicate()
|
||||
var temp = new_layers[Global.current_layer]
|
||||
new_layers[Global.current_layer] = new_layers[change]
|
||||
new_layers[change] = temp
|
||||
Global.undo_redo.create_action("Change Layer Order")
|
||||
for c in Global.canvases:
|
||||
var new_layers_canvas : Array = c.layers.duplicate()
|
||||
var temp_canvas = new_layers_canvas[Global.current_layer]
|
||||
new_layers_canvas[Global.current_layer] = new_layers_canvas[change]
|
||||
new_layers_canvas[change] = temp_canvas
|
||||
Global.undo_redo.add_do_property(c, "layers", new_layers_canvas)
|
||||
Global.undo_redo.add_undo_property(c, "layers", c.layers)
|
||||
|
||||
Global.undo_redo.add_do_property(Global, "current_layer", change)
|
||||
Global.undo_redo.add_do_property(Global, "layers", new_layers)
|
||||
Global.undo_redo.add_undo_property(Global, "layers", Global.layers)
|
||||
Global.undo_redo.add_undo_property(Global, "current_layer", Global.current_layer)
|
||||
|
||||
Global.undo_redo.add_undo_method(Global, "undo", [Global.canvas])
|
||||
Global.undo_redo.add_do_method(Global, "redo", [Global.canvas])
|
||||
Global.undo_redo.commit_action()
|
||||
|
||||
|
||||
func _on_MergeDownLayer_pressed() -> void:
|
||||
var new_layers : Array = Global.layers.duplicate(true)
|
||||
|
||||
Global.undos += 1
|
||||
Global.undo_redo.create_action("Merge Layer")
|
||||
for c in Global.canvases:
|
||||
var new_layers_canvas : Array = c.layers.duplicate(true)
|
||||
var selected_layer := Image.new()
|
||||
selected_layer.copy_from(new_layers_canvas[Global.current_layer][0])
|
||||
selected_layer.lock()
|
||||
|
||||
if c.layers[Global.current_layer][2] < 1: # If we have layer transparency
|
||||
for xx in selected_layer.get_size().x:
|
||||
for yy in selected_layer.get_size().y:
|
||||
var pixel_color : Color = selected_layer.get_pixel(xx, yy)
|
||||
var alpha : float = pixel_color.a * c.layers[Global.current_layer][2]
|
||||
selected_layer.set_pixel(xx, yy, Color(pixel_color.r, pixel_color.g, pixel_color.b, alpha))
|
||||
|
||||
var new_layer := Image.new()
|
||||
new_layer.copy_from(c.layers[Global.current_layer - 1][0])
|
||||
new_layer.lock()
|
||||
c.blend_rect(new_layer, selected_layer, Rect2(c.position, c.size), Vector2.ZERO)
|
||||
new_layers_canvas.remove(Global.current_layer)
|
||||
if !selected_layer.is_invisible() and Global.layers[Global.current_layer - 1][5].size() > 1 and (c in Global.layers[Global.current_layer - 1][5]):
|
||||
new_layers[Global.current_layer - 1][5].erase(c)
|
||||
var tex := ImageTexture.new()
|
||||
tex.create_from_image(new_layer, 0)
|
||||
new_layers_canvas[Global.current_layer - 1][0] = new_layer
|
||||
new_layers_canvas[Global.current_layer - 1][1] = tex
|
||||
else:
|
||||
Global.undo_redo.add_do_property(c.layers[Global.current_layer - 1][0], "data", new_layer.data)
|
||||
Global.undo_redo.add_undo_property(c.layers[Global.current_layer - 1][0], "data", c.layers[Global.current_layer - 1][0].data)
|
||||
|
||||
Global.undo_redo.add_do_property(c, "layers", new_layers_canvas)
|
||||
Global.undo_redo.add_undo_property(c, "layers", c.layers)
|
||||
|
||||
new_layers.remove(Global.current_layer)
|
||||
Global.undo_redo.add_do_property(Global, "current_layer", Global.current_layer - 1)
|
||||
Global.undo_redo.add_do_property(Global, "layers", new_layers)
|
||||
Global.undo_redo.add_undo_property(Global, "layers", Global.layers)
|
||||
Global.undo_redo.add_undo_property(Global, "current_layer", Global.current_layer)
|
||||
|
||||
Global.undo_redo.add_undo_method(Global, "undo", Global.canvases)
|
||||
Global.undo_redo.add_do_method(Global, "redo", Global.canvases)
|
||||
Global.undo_redo.commit_action()
|
||||
|
||||
|
||||
func _on_OpacitySlider_value_changed(value) -> void:
|
||||
Global.canvas.layers[Global.current_layer][2] = value / 100
|
||||
Global.layer_opacity_slider.value = value
|
||||
Global.layer_opacity_slider.value = value
|
||||
Global.layer_opacity_spinbox.value = value
|
||||
Global.canvas.update()
|
||||
|
||||
|
||||
func _on_OnionSkinningSettings_popup_hide() -> void:
|
||||
Global.can_draw = true
|
834
src/AnimationTimeline.tscn
Normal file
834
src/AnimationTimeline.tscn
Normal file
|
@ -0,0 +1,834 @@
|
|||
[gd_scene load_steps=51 format=2]
|
||||
|
||||
[ext_resource path="res://src/AnimationTimeline.gd" type="Script" id=1]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Layers/New_Layer.png" type="Texture" id=2]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Layers/New_Layer_Hover.png" type="Texture" id=3]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Layers/Delete_Layer.png" type="Texture" id=4]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Layers/Delete_Layer_Hover.png" type="Texture" id=5]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Layers/Delete_Layer_Disabled.png" type="Texture" id=6]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Layers/Move_Up.png" type="Texture" id=7]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Layers/Move_Up_Hover.png" type="Texture" id=8]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Layers/Move_Up_Disabled.png" type="Texture" id=9]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Layers/Move_Down.png" type="Texture" id=10]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Layers/Move_Down_Hover.png" type="Texture" id=11]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Layers/Move_Down_Disabled.png" type="Texture" id=12]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Layers/Clone_Layer.png" type="Texture" id=13]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Layers/Clone_Layer_Hover.png" type="Texture" id=14]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Layers/Merge_Down.png" type="Texture" id=15]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Layers/Merge_Down_Hover.png" type="Texture" id=16]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Layers/Merge_Down_Disabled.png" type="Texture" id=17]
|
||||
[ext_resource path="res://src/LayerButton.tscn" type="PackedScene" id=18]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Timeline/new_frame.png" type="Texture" id=19]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Timeline/remove_frame.png" type="Texture" id=20]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Timeline/go_to_first_frame.png" type="Texture" id=21]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Timeline/play.png" type="Texture" id=22]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Timeline/previous_frame.png" type="Texture" id=23]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Timeline/play_backwards.png" type="Texture" id=24]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Timeline/go_to_last_frame.png" type="Texture" id=25]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Timeline/next_frame.png" type="Texture" id=26]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Timeline/copy_frame.png" type="Texture" id=27]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Timeline/tag.png" type="Texture" id=28]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Timeline/onion_skinning_off.png" type="Texture" id=29]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Timeline/expandable.png" type="Texture" id=30]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Timeline/loop.png" type="Texture" id=31]
|
||||
[ext_resource path="res://src/Dialogs/FrameTagDialog.tscn" type="PackedScene" id=42]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[sub_resource type="InputEventKey" id=1]
|
||||
control = true
|
||||
command = true
|
||||
scancode = 16777229
|
||||
|
||||
[sub_resource type="ShortCut" id=2]
|
||||
shortcut = SubResource( 1 )
|
||||
|
||||
[sub_resource type="InputEventKey" id=3]
|
||||
control = true
|
||||
command = true
|
||||
scancode = 16777231
|
||||
|
||||
[sub_resource type="ShortCut" id=4]
|
||||
shortcut = SubResource( 3 )
|
||||
|
||||
[sub_resource type="InputEventKey" id=5]
|
||||
scancode = 16777247
|
||||
|
||||
[sub_resource type="ShortCut" id=6]
|
||||
shortcut = SubResource( 5 )
|
||||
|
||||
[sub_resource type="InputEventKey" id=7]
|
||||
scancode = 16777248
|
||||
|
||||
[sub_resource type="ShortCut" id=8]
|
||||
shortcut = SubResource( 7 )
|
||||
|
||||
[sub_resource type="InputEventKey" id=9]
|
||||
control = true
|
||||
command = true
|
||||
scancode = 16777233
|
||||
|
||||
[sub_resource type="ShortCut" id=10]
|
||||
shortcut = SubResource( 9 )
|
||||
|
||||
[sub_resource type="InputEventKey" id=11]
|
||||
control = true
|
||||
command = true
|
||||
scancode = 16777230
|
||||
|
||||
[sub_resource type="ShortCut" id=12]
|
||||
shortcut = SubResource( 11 )
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id=13]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id=14]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id=15]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id=16]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id=17]
|
||||
|
||||
[sub_resource type="Theme" id=18]
|
||||
HScrollBar/icons/decrement = null
|
||||
HScrollBar/icons/decrement_highlight = null
|
||||
HScrollBar/icons/increment = null
|
||||
HScrollBar/icons/increment_highlight = null
|
||||
HScrollBar/styles/grabber = SubResource( 13 )
|
||||
HScrollBar/styles/grabber_highlight = SubResource( 14 )
|
||||
HScrollBar/styles/grabber_pressed = SubResource( 15 )
|
||||
HScrollBar/styles/scroll = SubResource( 16 )
|
||||
HScrollBar/styles/scroll_focus = SubResource( 17 )
|
||||
|
||||
[node name="AnimationTimeline" type="Panel"]
|
||||
margin_top = 438.0
|
||||
margin_right = 704.0
|
||||
margin_bottom = 620.0
|
||||
rect_min_size = Vector2( 0, 200 )
|
||||
size_flags_horizontal = 3
|
||||
script = ExtResource( 1 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="AnimationContainer" type="HBoxContainer" parent="."]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="ForLayerButtons" type="VBoxContainer" parent="AnimationContainer"]
|
||||
margin_right = 68.0
|
||||
margin_bottom = 200.0
|
||||
|
||||
[node name="SpacerControl" type="Control" parent="AnimationContainer/ForLayerButtons"]
|
||||
margin_right = 68.0
|
||||
margin_bottom = 30.0
|
||||
rect_min_size = Vector2( 0, 30 )
|
||||
|
||||
[node name="LayerButtons" type="GridContainer" parent="AnimationContainer/ForLayerButtons"]
|
||||
margin_top = 34.0
|
||||
margin_right = 68.0
|
||||
margin_bottom = 138.0
|
||||
size_flags_vertical = 0
|
||||
custom_constants/vseparation = 4
|
||||
custom_constants/hseparation = 4
|
||||
columns = 2
|
||||
|
||||
[node name="AddLayer" type="TextureButton" parent="AnimationContainer/ForLayerButtons/LayerButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_right = 32.0
|
||||
margin_bottom = 32.0
|
||||
hint_tooltip = "Create a new layer"
|
||||
focus_mode = 0
|
||||
mouse_default_cursor_shape = 2
|
||||
texture_normal = ExtResource( 2 )
|
||||
texture_hover = ExtResource( 3 )
|
||||
|
||||
[node name="RemoveLayer" type="TextureButton" parent="AnimationContainer/ForLayerButtons/LayerButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_left = 36.0
|
||||
margin_right = 68.0
|
||||
margin_bottom = 32.0
|
||||
hint_tooltip = "Remove current layer"
|
||||
focus_mode = 0
|
||||
mouse_default_cursor_shape = 8
|
||||
disabled = true
|
||||
texture_normal = ExtResource( 4 )
|
||||
texture_hover = ExtResource( 5 )
|
||||
texture_disabled = ExtResource( 6 )
|
||||
|
||||
[node name="MoveUpLayer" type="TextureButton" parent="AnimationContainer/ForLayerButtons/LayerButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_top = 36.0
|
||||
margin_right = 32.0
|
||||
margin_bottom = 68.0
|
||||
hint_tooltip = "Move up the current layer"
|
||||
focus_mode = 0
|
||||
mouse_default_cursor_shape = 8
|
||||
disabled = true
|
||||
texture_normal = ExtResource( 7 )
|
||||
texture_hover = ExtResource( 8 )
|
||||
texture_disabled = ExtResource( 9 )
|
||||
|
||||
[node name="MoveDownLayer" type="TextureButton" parent="AnimationContainer/ForLayerButtons/LayerButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_left = 36.0
|
||||
margin_top = 36.0
|
||||
margin_right = 68.0
|
||||
margin_bottom = 68.0
|
||||
hint_tooltip = "Move down the current layer"
|
||||
focus_mode = 0
|
||||
mouse_default_cursor_shape = 8
|
||||
disabled = true
|
||||
texture_normal = ExtResource( 10 )
|
||||
texture_hover = ExtResource( 11 )
|
||||
texture_disabled = ExtResource( 12 )
|
||||
|
||||
[node name="CloneLayer" type="TextureButton" parent="AnimationContainer/ForLayerButtons/LayerButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_top = 72.0
|
||||
margin_right = 32.0
|
||||
margin_bottom = 104.0
|
||||
hint_tooltip = "Clone current layer"
|
||||
focus_mode = 0
|
||||
mouse_default_cursor_shape = 2
|
||||
texture_normal = ExtResource( 13 )
|
||||
texture_hover = ExtResource( 14 )
|
||||
|
||||
[node name="MergeDownLayer" type="TextureButton" parent="AnimationContainer/ForLayerButtons/LayerButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_left = 36.0
|
||||
margin_top = 72.0
|
||||
margin_right = 68.0
|
||||
margin_bottom = 104.0
|
||||
hint_tooltip = "Merge current layer with the one below"
|
||||
focus_mode = 0
|
||||
mouse_default_cursor_shape = 8
|
||||
disabled = true
|
||||
texture_normal = ExtResource( 15 )
|
||||
texture_hover = ExtResource( 16 )
|
||||
texture_disabled = ExtResource( 17 )
|
||||
|
||||
[node name="SpacerControl" type="Control" parent="AnimationContainer"]
|
||||
margin_left = 72.0
|
||||
margin_right = 88.0
|
||||
margin_bottom = 200.0
|
||||
rect_min_size = Vector2( 16, 0 )
|
||||
|
||||
[node name="TimelineContainer" type="VBoxContainer" parent="AnimationContainer"]
|
||||
margin_left = 92.0
|
||||
margin_right = 696.0
|
||||
margin_bottom = 200.0
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="SpacerControl" type="Control" parent="AnimationContainer/TimelineContainer"]
|
||||
margin_right = 604.0
|
||||
|
||||
[node name="AnimationButtons" type="HBoxContainer" parent="AnimationContainer/TimelineContainer"]
|
||||
margin_top = 4.0
|
||||
margin_right = 604.0
|
||||
margin_bottom = 28.0
|
||||
rect_min_size = Vector2( 0, 24 )
|
||||
|
||||
[node name="CurrentFrame" type="Label" parent="AnimationContainer/TimelineContainer/AnimationButtons"]
|
||||
margin_top = 5.0
|
||||
margin_right = 150.0
|
||||
margin_bottom = 19.0
|
||||
rect_min_size = Vector2( 150, 0 )
|
||||
text = "Current frame: 1/1"
|
||||
|
||||
[node name="AddFrame" type="Button" parent="AnimationContainer/TimelineContainer/AnimationButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_left = 154.0
|
||||
margin_right = 174.0
|
||||
margin_bottom = 20.0
|
||||
rect_min_size = Vector2( 20, 0 )
|
||||
hint_tooltip = "Add a new frame"
|
||||
focus_mode = 0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 0
|
||||
size_flags_vertical = 0
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="AnimationContainer/TimelineContainer/AnimationButtons/AddFrame"]
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
margin_left = -6.0
|
||||
margin_top = -6.0
|
||||
margin_right = 6.0
|
||||
margin_bottom = 6.0
|
||||
texture = ExtResource( 19 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="DeleteFrame" type="Button" parent="AnimationContainer/TimelineContainer/AnimationButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_left = 178.0
|
||||
margin_right = 198.0
|
||||
margin_bottom = 20.0
|
||||
rect_min_size = Vector2( 20, 0 )
|
||||
hint_tooltip = "Remove Frame"
|
||||
focus_mode = 0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 0
|
||||
size_flags_vertical = 0
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="AnimationContainer/TimelineContainer/AnimationButtons/DeleteFrame"]
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
margin_left = -6.0
|
||||
margin_top = -1.0
|
||||
margin_right = 6.0
|
||||
margin_bottom = 1.0
|
||||
size_flags_horizontal = 0
|
||||
size_flags_vertical = 0
|
||||
texture = ExtResource( 20 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="CopyFrame" type="Button" parent="AnimationContainer/TimelineContainer/AnimationButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_left = 202.0
|
||||
margin_right = 222.0
|
||||
margin_bottom = 20.0
|
||||
rect_min_size = Vector2( 20, 0 )
|
||||
hint_tooltip = "Clone Frame"
|
||||
focus_mode = 0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 0
|
||||
size_flags_vertical = 0
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="AnimationContainer/TimelineContainer/AnimationButtons/CopyFrame"]
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
margin_left = -5.0
|
||||
margin_top = -7.0
|
||||
margin_right = 5.0
|
||||
margin_bottom = 7.0
|
||||
texture = ExtResource( 27 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="FrameTagButton" type="Button" parent="AnimationContainer/TimelineContainer/AnimationButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_left = 226.0
|
||||
margin_right = 246.0
|
||||
margin_bottom = 20.0
|
||||
rect_min_size = Vector2( 20, 0 )
|
||||
hint_tooltip = "Manage frame tags"
|
||||
focus_mode = 0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 0
|
||||
size_flags_vertical = 0
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="AnimationContainer/TimelineContainer/AnimationButtons/FrameTagButton"]
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
margin_left = -7.0
|
||||
margin_top = -7.0
|
||||
margin_right = 7.0
|
||||
margin_bottom = 7.0
|
||||
texture = ExtResource( 28 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="PlaybackButtons" type="HBoxContainer" parent="AnimationContainer/TimelineContainer/AnimationButtons"]
|
||||
margin_left = 286.0
|
||||
margin_right = 426.0
|
||||
margin_bottom = 24.0
|
||||
size_flags_horizontal = 6
|
||||
|
||||
[node name="FirstFrame" type="Button" parent="AnimationContainer/TimelineContainer/AnimationButtons/PlaybackButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_right = 20.0
|
||||
margin_bottom = 20.0
|
||||
rect_min_size = Vector2( 20, 0 )
|
||||
hint_tooltip = "FIRSTFRAME_HT"
|
||||
focus_mode = 0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_vertical = 0
|
||||
shortcut_in_tooltip = false
|
||||
shortcut = SubResource( 2 )
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="AnimationContainer/TimelineContainer/AnimationButtons/PlaybackButtons/FirstFrame"]
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
margin_left = -5.5
|
||||
margin_top = -6.0
|
||||
margin_right = 5.5
|
||||
margin_bottom = 6.0
|
||||
texture = ExtResource( 21 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="PreviousFrame" type="Button" parent="AnimationContainer/TimelineContainer/AnimationButtons/PlaybackButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_left = 24.0
|
||||
margin_right = 44.0
|
||||
margin_bottom = 20.0
|
||||
rect_min_size = Vector2( 20, 0 )
|
||||
hint_tooltip = "PREVIOUSFRAME_HT"
|
||||
focus_mode = 0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_vertical = 0
|
||||
shortcut_in_tooltip = false
|
||||
shortcut = SubResource( 4 )
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="AnimationContainer/TimelineContainer/AnimationButtons/PlaybackButtons/PreviousFrame"]
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
margin_left = -5.5
|
||||
margin_top = -6.0
|
||||
margin_right = 5.5
|
||||
margin_bottom = 6.0
|
||||
texture = ExtResource( 23 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="PlayBackwards" type="Button" parent="AnimationContainer/TimelineContainer/AnimationButtons/PlaybackButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_left = 48.0
|
||||
margin_right = 68.0
|
||||
margin_bottom = 20.0
|
||||
rect_min_size = Vector2( 20, 0 )
|
||||
hint_tooltip = "PLAYBACKWARDS_HT"
|
||||
focus_mode = 0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_vertical = 0
|
||||
toggle_mode = true
|
||||
shortcut_in_tooltip = false
|
||||
shortcut = SubResource( 6 )
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="AnimationContainer/TimelineContainer/AnimationButtons/PlaybackButtons/PlayBackwards"]
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
margin_left = -4.0
|
||||
margin_top = -6.0
|
||||
margin_right = 3.0
|
||||
margin_bottom = 6.0
|
||||
texture = ExtResource( 24 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="PlayForward" type="Button" parent="AnimationContainer/TimelineContainer/AnimationButtons/PlaybackButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_left = 72.0
|
||||
margin_right = 92.0
|
||||
margin_bottom = 20.0
|
||||
rect_min_size = Vector2( 20, 0 )
|
||||
hint_tooltip = "PLAYFORWARD_HT"
|
||||
focus_mode = 0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 0
|
||||
size_flags_vertical = 0
|
||||
toggle_mode = true
|
||||
shortcut_in_tooltip = false
|
||||
shortcut = SubResource( 8 )
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="AnimationContainer/TimelineContainer/AnimationButtons/PlaybackButtons/PlayForward"]
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
margin_left = -3.5
|
||||
margin_top = -6.0
|
||||
margin_right = 3.5
|
||||
margin_bottom = 6.0
|
||||
texture = ExtResource( 22 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="NextFrame" type="Button" parent="AnimationContainer/TimelineContainer/AnimationButtons/PlaybackButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_left = 96.0
|
||||
margin_right = 116.0
|
||||
margin_bottom = 20.0
|
||||
rect_min_size = Vector2( 20, 0 )
|
||||
hint_tooltip = "NEXTFRAME_HT"
|
||||
focus_mode = 0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_vertical = 0
|
||||
shortcut_in_tooltip = false
|
||||
shortcut = SubResource( 10 )
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="AnimationContainer/TimelineContainer/AnimationButtons/PlaybackButtons/NextFrame"]
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
margin_left = -5.5
|
||||
margin_top = -6.0
|
||||
margin_right = 5.5
|
||||
margin_bottom = 6.0
|
||||
texture = ExtResource( 26 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="LastFrame" type="Button" parent="AnimationContainer/TimelineContainer/AnimationButtons/PlaybackButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_left = 120.0
|
||||
margin_right = 140.0
|
||||
margin_bottom = 20.0
|
||||
rect_min_size = Vector2( 20, 0 )
|
||||
hint_tooltip = "LASTFRAME_HT"
|
||||
focus_mode = 0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_vertical = 0
|
||||
shortcut_in_tooltip = false
|
||||
shortcut = SubResource( 12 )
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="AnimationContainer/TimelineContainer/AnimationButtons/PlaybackButtons/LastFrame"]
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
margin_left = -5.5
|
||||
margin_top = -6.0
|
||||
margin_right = 5.5
|
||||
margin_bottom = 6.0
|
||||
texture = ExtResource( 25 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="LoopButtons" type="HBoxContainer" parent="AnimationContainer/TimelineContainer/AnimationButtons"]
|
||||
margin_left = 466.0
|
||||
margin_right = 604.0
|
||||
margin_bottom = 24.0
|
||||
size_flags_horizontal = 0
|
||||
|
||||
[node name="OnionSkinningSettings" type="Button" parent="AnimationContainer/TimelineContainer/AnimationButtons/LoopButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_right = 12.0
|
||||
margin_bottom = 20.0
|
||||
hint_tooltip = "Onion Skinning settings"
|
||||
focus_mode = 0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 0
|
||||
size_flags_vertical = 0
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="AnimationContainer/TimelineContainer/AnimationButtons/LoopButtons/OnionSkinningSettings"]
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
margin_left = -3.0
|
||||
margin_top = -6.0
|
||||
margin_right = 3.0
|
||||
margin_bottom = 6.0
|
||||
size_flags_horizontal = 0
|
||||
size_flags_vertical = 0
|
||||
texture = ExtResource( 30 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="OnionSkinning" type="Button" parent="AnimationContainer/TimelineContainer/AnimationButtons/LoopButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_left = 16.0
|
||||
margin_right = 36.0
|
||||
margin_bottom = 20.0
|
||||
rect_min_size = Vector2( 20, 0 )
|
||||
hint_tooltip = "Enable/disable Onion Skinning"
|
||||
focus_mode = 0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_vertical = 0
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="AnimationContainer/TimelineContainer/AnimationButtons/LoopButtons/OnionSkinning"]
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
margin_left = -7.0
|
||||
margin_top = -7.0
|
||||
margin_right = 7.0
|
||||
margin_bottom = 7.0
|
||||
size_flags_horizontal = 0
|
||||
size_flags_vertical = 0
|
||||
texture = ExtResource( 29 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="LoopAnim" type="Button" parent="AnimationContainer/TimelineContainer/AnimationButtons/LoopButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_left = 40.0
|
||||
margin_right = 60.0
|
||||
margin_bottom = 20.0
|
||||
rect_min_size = Vector2( 20, 0 )
|
||||
hint_tooltip = "Cycle loop"
|
||||
focus_mode = 0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_vertical = 0
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="AnimationContainer/TimelineContainer/AnimationButtons/LoopButtons/LoopAnim"]
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
margin_left = -7.0
|
||||
margin_top = -7.0
|
||||
margin_right = 7.0
|
||||
margin_bottom = 7.0
|
||||
texture = ExtResource( 31 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="FPSValue" type="SpinBox" parent="AnimationContainer/TimelineContainer/AnimationButtons/LoopButtons"]
|
||||
margin_left = 64.0
|
||||
margin_right = 138.0
|
||||
margin_bottom = 24.0
|
||||
hint_tooltip = "How many frames per second should the animation preview be?
|
||||
The more FPS, the faster the animation plays."
|
||||
mouse_default_cursor_shape = 2
|
||||
min_value = 0.1
|
||||
step = 0.1
|
||||
value = 6.0
|
||||
align = 1
|
||||
suffix = "FPS"
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="AnimationContainer/TimelineContainer"]
|
||||
margin_top = 32.0
|
||||
margin_right = 604.0
|
||||
margin_bottom = 36.0
|
||||
|
||||
[node name="OpacityAndTagContainer" type="HBoxContainer" parent="AnimationContainer/TimelineContainer"]
|
||||
margin_top = 40.0
|
||||
margin_right = 604.0
|
||||
margin_bottom = 72.0
|
||||
custom_constants/separation = 2
|
||||
|
||||
[node name="OpacityContainer" type="HBoxContainer" parent="AnimationContainer/TimelineContainer/OpacityAndTagContainer"]
|
||||
margin_right = 214.0
|
||||
margin_bottom = 32.0
|
||||
rect_min_size = Vector2( 214, 0 )
|
||||
|
||||
[node name="OpacityLabel" type="Label" parent="AnimationContainer/TimelineContainer/OpacityAndTagContainer/OpacityContainer"]
|
||||
margin_right = 53.0
|
||||
margin_bottom = 32.0
|
||||
size_flags_horizontal = 0
|
||||
size_flags_vertical = 1
|
||||
text = "Opacity:"
|
||||
valign = 1
|
||||
|
||||
[node name="OpacitySlider" type="HSlider" parent="AnimationContainer/TimelineContainer/OpacityAndTagContainer/OpacityContainer"]
|
||||
margin_left = 57.0
|
||||
margin_right = 136.0
|
||||
margin_bottom = 32.0
|
||||
focus_mode = 0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 1
|
||||
value = 100.0
|
||||
ticks_on_borders = true
|
||||
|
||||
[node name="OpacitySpinBox" type="SpinBox" parent="AnimationContainer/TimelineContainer/OpacityAndTagContainer/OpacityContainer"]
|
||||
margin_left = 140.0
|
||||
margin_top = 4.0
|
||||
margin_right = 214.0
|
||||
margin_bottom = 28.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_vertical = 4
|
||||
value = 100.0
|
||||
align = 1
|
||||
|
||||
[node name="TagScroll" type="ScrollContainer" parent="AnimationContainer/TimelineContainer/OpacityAndTagContainer"]
|
||||
margin_left = 216.0
|
||||
margin_right = 604.0
|
||||
margin_bottom = 32.0
|
||||
rect_min_size = Vector2( 0, 32 )
|
||||
size_flags_horizontal = 3
|
||||
theme = SubResource( 18 )
|
||||
scroll_vertical_enabled = false
|
||||
|
||||
[node name="TagContainer" type="Control" parent="AnimationContainer/TimelineContainer/OpacityAndTagContainer/TagScroll"]
|
||||
|
||||
[node name="TimelineScroll" type="ScrollContainer" parent="AnimationContainer/TimelineContainer"]
|
||||
margin_top = 76.0
|
||||
margin_right = 604.0
|
||||
margin_bottom = 200.0
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="LayersAndFrames" type="HBoxContainer" parent="AnimationContainer/TimelineContainer/TimelineScroll"]
|
||||
margin_right = 252.0
|
||||
margin_bottom = 124.0
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="LayerVBoxCont" type="VBoxContainer" parent="AnimationContainer/TimelineContainer/TimelineScroll/LayersAndFrames"]
|
||||
margin_right = 212.0
|
||||
margin_bottom = 124.0
|
||||
|
||||
[node name="LayerLabel" type="Label" parent="AnimationContainer/TimelineContainer/TimelineScroll/LayersAndFrames/LayerVBoxCont"]
|
||||
margin_right = 212.0
|
||||
margin_bottom = 16.0
|
||||
rect_min_size = Vector2( 0, 16 )
|
||||
text = "Layers"
|
||||
align = 1
|
||||
valign = 1
|
||||
|
||||
[node name="LayersContainer" type="VBoxContainer" parent="AnimationContainer/TimelineContainer/TimelineScroll/LayersAndFrames/LayerVBoxCont"]
|
||||
margin_top = 20.0
|
||||
margin_right = 212.0
|
||||
margin_bottom = 56.0
|
||||
|
||||
[node name="LayerContainer" parent="AnimationContainer/TimelineContainer/TimelineScroll/LayersAndFrames/LayerVBoxCont/LayersContainer" instance=ExtResource( 18 )]
|
||||
margin_right = 212.0
|
||||
|
||||
[node name="FrameButtonsAndIds" type="VBoxContainer" parent="AnimationContainer/TimelineContainer/TimelineScroll/LayersAndFrames"]
|
||||
margin_left = 216.0
|
||||
margin_right = 252.0
|
||||
margin_bottom = 124.0
|
||||
|
||||
[node name="FrameIDs" type="HBoxContainer" parent="AnimationContainer/TimelineContainer/TimelineScroll/LayersAndFrames/FrameButtonsAndIds"]
|
||||
margin_right = 36.0
|
||||
margin_bottom = 16.0
|
||||
rect_min_size = Vector2( 0, 16 )
|
||||
|
||||
[node name="Label" type="Label" parent="AnimationContainer/TimelineContainer/TimelineScroll/LayersAndFrames/FrameButtonsAndIds/FrameIDs"]
|
||||
margin_top = 1.0
|
||||
margin_right = 36.0
|
||||
margin_bottom = 15.0
|
||||
rect_min_size = Vector2( 36, 0 )
|
||||
text = "1"
|
||||
align = 1
|
||||
|
||||
[node name="FramesContainer" type="VBoxContainer" parent="AnimationContainer/TimelineContainer/TimelineScroll/LayersAndFrames/FrameButtonsAndIds"]
|
||||
margin_top = 20.0
|
||||
margin_right = 36.0
|
||||
margin_bottom = 20.0
|
||||
|
||||
[node name="VSeparator" type="VSeparator" parent="AnimationContainer"]
|
||||
margin_left = 700.0
|
||||
margin_right = 704.0
|
||||
margin_bottom = 200.0
|
||||
|
||||
[node name="AnimationTimer" type="Timer" parent="."]
|
||||
|
||||
[node name="OnionSkinningSettings" type="WindowDialog" parent="."]
|
||||
rect_min_size = Vector2( 220, 126 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="OnionSkinningButtons" type="VBoxContainer" parent="OnionSkinningSettings"]
|
||||
anchor_top = 0.5
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 0.5
|
||||
margin_left = 4.0
|
||||
margin_top = -58.0
|
||||
margin_right = -4.0
|
||||
margin_bottom = 58.0
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="OnionSkinningPast" type="Label" parent="OnionSkinningSettings/OnionSkinningButtons"]
|
||||
margin_right = 212.0
|
||||
margin_bottom = 14.0
|
||||
text = "Past Frames"
|
||||
|
||||
[node name="PastOnionSkinning" type="SpinBox" parent="OnionSkinningSettings/OnionSkinningButtons"]
|
||||
margin_top = 18.0
|
||||
margin_right = 212.0
|
||||
margin_bottom = 42.0
|
||||
mouse_default_cursor_shape = 2
|
||||
min_value = 1.0
|
||||
value = 1.0
|
||||
align = 1
|
||||
|
||||
[node name="OnionSkinningFuture" type="Label" parent="OnionSkinningSettings/OnionSkinningButtons"]
|
||||
margin_top = 46.0
|
||||
margin_right = 212.0
|
||||
margin_bottom = 60.0
|
||||
text = "Future Frames"
|
||||
|
||||
[node name="FutureOnionSkinning" type="SpinBox" parent="OnionSkinningSettings/OnionSkinningButtons"]
|
||||
margin_top = 64.0
|
||||
margin_right = 212.0
|
||||
margin_bottom = 88.0
|
||||
mouse_default_cursor_shape = 2
|
||||
min_value = 1.0
|
||||
value = 1.0
|
||||
align = 1
|
||||
|
||||
[node name="BlueRedMode" type="CheckBox" parent="OnionSkinningSettings/OnionSkinningButtons"]
|
||||
margin_top = 92.0
|
||||
margin_right = 212.0
|
||||
margin_bottom = 116.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Blue-Red Mode"
|
||||
|
||||
[node name="FrameTagDialog" parent="." instance=ExtResource( 42 )]
|
||||
[connection signal="pressed" from="AnimationContainer/ForLayerButtons/LayerButtons/AddLayer" to="." method="add_layer" binds= [ true ]]
|
||||
[connection signal="pressed" from="AnimationContainer/ForLayerButtons/LayerButtons/RemoveLayer" to="." method="_on_RemoveLayer_pressed"]
|
||||
[connection signal="pressed" from="AnimationContainer/ForLayerButtons/LayerButtons/MoveUpLayer" to="." method="change_layer_order" binds= [ 1 ]]
|
||||
[connection signal="pressed" from="AnimationContainer/ForLayerButtons/LayerButtons/MoveDownLayer" to="." method="change_layer_order" binds= [ -1 ]]
|
||||
[connection signal="pressed" from="AnimationContainer/ForLayerButtons/LayerButtons/CloneLayer" to="." method="add_layer" binds= [ false ]]
|
||||
[connection signal="pressed" from="AnimationContainer/ForLayerButtons/LayerButtons/MergeDownLayer" to="." method="_on_MergeDownLayer_pressed"]
|
||||
[connection signal="pressed" from="AnimationContainer/TimelineContainer/AnimationButtons/AddFrame" to="." method="add_frame"]
|
||||
[connection signal="pressed" from="AnimationContainer/TimelineContainer/AnimationButtons/DeleteFrame" to="." method="_on_DeleteFrame_pressed"]
|
||||
[connection signal="pressed" from="AnimationContainer/TimelineContainer/AnimationButtons/CopyFrame" to="." method="_on_CopyFrame_pressed"]
|
||||
[connection signal="pressed" from="AnimationContainer/TimelineContainer/AnimationButtons/FrameTagButton" to="." method="_on_FrameTagButton_pressed"]
|
||||
[connection signal="pressed" from="AnimationContainer/TimelineContainer/AnimationButtons/PlaybackButtons/FirstFrame" to="." method="_on_FirstFrame_pressed"]
|
||||
[connection signal="pressed" from="AnimationContainer/TimelineContainer/AnimationButtons/PlaybackButtons/PreviousFrame" to="." method="_on_PreviousFrame_pressed"]
|
||||
[connection signal="toggled" from="AnimationContainer/TimelineContainer/AnimationButtons/PlaybackButtons/PlayBackwards" to="." method="_on_PlayBackwards_toggled"]
|
||||
[connection signal="toggled" from="AnimationContainer/TimelineContainer/AnimationButtons/PlaybackButtons/PlayForward" to="." method="_on_PlayForward_toggled"]
|
||||
[connection signal="pressed" from="AnimationContainer/TimelineContainer/AnimationButtons/PlaybackButtons/NextFrame" to="." method="_on_NextFrame_pressed"]
|
||||
[connection signal="pressed" from="AnimationContainer/TimelineContainer/AnimationButtons/PlaybackButtons/LastFrame" to="." method="_on_LastFrame_pressed"]
|
||||
[connection signal="pressed" from="AnimationContainer/TimelineContainer/AnimationButtons/LoopButtons/OnionSkinningSettings" to="." method="_on_OnionSkinningSettings_pressed"]
|
||||
[connection signal="pressed" from="AnimationContainer/TimelineContainer/AnimationButtons/LoopButtons/OnionSkinning" to="." method="_on_OnionSkinning_pressed"]
|
||||
[connection signal="pressed" from="AnimationContainer/TimelineContainer/AnimationButtons/LoopButtons/LoopAnim" to="." method="_on_LoopAnim_pressed"]
|
||||
[connection signal="value_changed" from="AnimationContainer/TimelineContainer/AnimationButtons/LoopButtons/FPSValue" to="." method="_on_FPSValue_value_changed"]
|
||||
[connection signal="value_changed" from="AnimationContainer/TimelineContainer/OpacityAndTagContainer/OpacityContainer/OpacitySlider" to="." method="_on_OpacitySlider_value_changed"]
|
||||
[connection signal="value_changed" from="AnimationContainer/TimelineContainer/OpacityAndTagContainer/OpacityContainer/OpacitySpinBox" to="." method="_on_OpacitySlider_value_changed"]
|
||||
[connection signal="timeout" from="AnimationTimer" to="." method="_on_AnimationTimer_timeout"]
|
||||
[connection signal="popup_hide" from="OnionSkinningSettings" to="." method="_on_OnionSkinningSettings_popup_hide"]
|
||||
[connection signal="value_changed" from="OnionSkinningSettings/OnionSkinningButtons/PastOnionSkinning" to="." method="_on_PastOnionSkinning_value_changed"]
|
||||
[connection signal="value_changed" from="OnionSkinningSettings/OnionSkinningButtons/FutureOnionSkinning" to="." method="_on_FutureOnionSkinning_value_changed"]
|
||||
[connection signal="toggled" from="OnionSkinningSettings/OnionSkinningButtons/BlueRedMode" to="." method="_on_BlueRedMode_toggled"]
|
1160
src/Autoload/Global.gd
Normal file
1160
src/Autoload/Global.gd
Normal file
File diff suppressed because it is too large
Load diff
336
src/Autoload/Import.gd
Normal file
336
src/Autoload/Import.gd
Normal file
|
@ -0,0 +1,336 @@
|
|||
extends Node
|
||||
|
||||
|
||||
# Get hold of the brushes, including random brushes (subdirectories and % files
|
||||
# in them, non % files get loaded independently.) nyaaa
|
||||
# Returns a list of [
|
||||
# [non random single png files in the root subdir],
|
||||
# {
|
||||
# map of subdirectories to lists of files for
|
||||
# the randomised brush - if a directory contains no
|
||||
# randomised files then it is not included in this.
|
||||
# },
|
||||
# {
|
||||
# map of subdirectories to lists of files inside of them
|
||||
# that are not for randomised brushes.
|
||||
# }
|
||||
# ]
|
||||
# The separation of nonrandomised and randomised files
|
||||
# in subdirectories allows different XDG_DATA_DIR overriding
|
||||
# for each nyaa.
|
||||
#
|
||||
# Returns null if the directory gave an error opening.
|
||||
#
|
||||
func get_brush_files_from_directory(directory: String): # -> Array
|
||||
var base_png_files := [] # list of files in the base directory
|
||||
var subdirectories := [] # list of subdirectories to process.
|
||||
|
||||
var randomised_subdir_files_map : Dictionary = {}
|
||||
var nonrandomised_subdir_files_map : Dictionary = {}
|
||||
|
||||
var main_directory : Directory = Directory.new()
|
||||
var err := main_directory.open(directory)
|
||||
if err != OK:
|
||||
return null
|
||||
|
||||
# Build first the list of base png files and all subdirectories to
|
||||
# scan later (skip navigational . and ..)
|
||||
main_directory.list_dir_begin(true)
|
||||
var fname : String = main_directory.get_next()
|
||||
while fname != "":
|
||||
if main_directory.current_is_dir():
|
||||
subdirectories.append(fname)
|
||||
else: # Filter for pngs
|
||||
if fname.get_extension().to_lower() == "png":
|
||||
base_png_files.append(fname)
|
||||
|
||||
# go to next
|
||||
fname = main_directory.get_next()
|
||||
main_directory.list_dir_end()
|
||||
|
||||
# Now we iterate over subdirectories!
|
||||
for subdirectory in subdirectories:
|
||||
var the_directory : Directory = Directory.new()
|
||||
|
||||
# Holds names of files that make this
|
||||
# a component of a randomised brush ^.^
|
||||
var randomised_files := []
|
||||
|
||||
# Non-randomise-indicated image files
|
||||
var non_randomised_files := []
|
||||
|
||||
the_directory.open(directory.plus_file(subdirectory))
|
||||
the_directory.list_dir_begin(true)
|
||||
var curr_file := the_directory.get_next()
|
||||
|
||||
while curr_file != "":
|
||||
# only do stuff if we are actually dealing with a file
|
||||
# and png one at that nya
|
||||
if !the_directory.current_is_dir() and curr_file.get_extension().to_lower() == "png":
|
||||
# if we are a random element, add
|
||||
if "%" in curr_file:
|
||||
randomised_files.append(curr_file)
|
||||
else:
|
||||
non_randomised_files.append(curr_file)
|
||||
curr_file = the_directory.get_next()
|
||||
|
||||
the_directory.list_dir_end()
|
||||
|
||||
# Add these to the maps nyaa
|
||||
if len(randomised_files) > 0:
|
||||
randomised_subdir_files_map[subdirectory] = randomised_files
|
||||
if len(non_randomised_files) > 0:
|
||||
nonrandomised_subdir_files_map[subdirectory] = non_randomised_files
|
||||
# We are done generating the maps!
|
||||
return [base_png_files, randomised_subdir_files_map, nonrandomised_subdir_files_map]
|
||||
|
||||
|
||||
# Add a randomised brush from the given list of files as a source.
|
||||
# The tooltip name is what shows up on the tooltip
|
||||
# and is probably in this case the name of the containing
|
||||
# randomised directory.
|
||||
func add_randomised_brush(fpaths : Array, tooltip_name : String) -> void:
|
||||
# Attempt to load the images from the file paths.
|
||||
var loaded_images : Array = []
|
||||
for filen in fpaths:
|
||||
var image := Image.new()
|
||||
var err := image.load(filen)
|
||||
if err == OK:
|
||||
image.convert(Image.FORMAT_RGBA8)
|
||||
loaded_images.append(image)
|
||||
|
||||
# If any images were successfully loaded, then
|
||||
# we create the randomised brush button, copied
|
||||
# from find_brushes.
|
||||
|
||||
if len(loaded_images) > 0: # actually have images
|
||||
# to use.
|
||||
# take initial image...
|
||||
var first_image : Image = loaded_images.pop_front()
|
||||
|
||||
# The index which this random brush will be at
|
||||
var next_random_brush_index := Global.file_brush_container.get_child_count()
|
||||
|
||||
Global.custom_brushes.append(first_image)
|
||||
Global.create_brush_button(first_image, Global.Brush_Types.RANDOM_FILE, tooltip_name)
|
||||
# # Process the rest
|
||||
for remaining_image in loaded_images:
|
||||
var brush_button = Global.file_brush_container.get_child(next_random_brush_index)
|
||||
brush_button.random_brushes.append(remaining_image)
|
||||
|
||||
# Add a plain brush from the given path to the list of brushes.
|
||||
# Taken, again, from find_brushes
|
||||
func add_plain_brush(path: String, tooltip_name: String) -> void:
|
||||
var image := Image.new()
|
||||
var err := image.load(path)
|
||||
if err != OK:
|
||||
return
|
||||
# do the standard conversion thing...
|
||||
image.convert(Image.FORMAT_RGBA8)
|
||||
Global.custom_brushes.append(image)
|
||||
Global.create_brush_button(image, Global.Brush_Types.FILE, tooltip_name)
|
||||
|
||||
|
||||
# Import brushes, in priority order, from the paths in question in priority order
|
||||
# i.e. with an override system
|
||||
# We use a very particular override system here where, for randomised brushes
|
||||
# the directories containing them get overridden, but for nonrandomised files
|
||||
# (including in directories containing randomised components too), the override
|
||||
# is on a file-by-file basis nyaaaa ^.^
|
||||
func import_brushes(priority_ordered_search_path: Array) -> void:
|
||||
# Maps for files in the base directory (name : true)
|
||||
var processed_basedir_paths : Dictionary = {}
|
||||
var randomised_brush_subdirectories : Dictionary = {}
|
||||
# Map from a subdirectory to a map similar to processed_basedir_files
|
||||
# i.e. once a filename has been dealt with, set it to true.
|
||||
var processed_subdir_paths : Dictionary = {}
|
||||
|
||||
# Sets of results of get_brush_files_from_directory
|
||||
var all_available_paths : Array = []
|
||||
for directory in priority_ordered_search_path:
|
||||
all_available_paths.append(get_brush_files_from_directory(directory))
|
||||
|
||||
# Now to process. Note these are in order of the
|
||||
# priority, as intended nyaa :)
|
||||
for i in range(len(all_available_paths)):
|
||||
var available_brush_file_information = all_available_paths[i]
|
||||
var current_main_directory: String = priority_ordered_search_path[i]
|
||||
if available_brush_file_information != null:
|
||||
# The brush files in the main directory
|
||||
var main_directory_file_paths : Array = available_brush_file_information[0]
|
||||
# The subdirectory/list-of-randomised-brush-files
|
||||
# map for this directory
|
||||
var randomised_brush_subdirectory_map : Dictionary = available_brush_file_information[1]
|
||||
# Map for subdirectories to non-randomised-brush files nyaa
|
||||
var nonrandomised_brush_subdirectory_map : Dictionary = available_brush_file_information[2]
|
||||
|
||||
# Iterate over components and do stuff with them! nyaa
|
||||
# first for the main directory path...
|
||||
for subfile in main_directory_file_paths:
|
||||
if not (subfile in processed_basedir_paths):
|
||||
add_plain_brush(
|
||||
current_main_directory.plus_file(subfile),
|
||||
subfile.get_basename()
|
||||
)
|
||||
processed_basedir_paths[subfile] = true
|
||||
|
||||
# Iterate over the randomised brush files nyaa
|
||||
for randomised_subdir in randomised_brush_subdirectory_map:
|
||||
if not (randomised_subdir in randomised_brush_subdirectories):
|
||||
var full_paths := []
|
||||
# glue the proper path onto the single file names in the
|
||||
# random brush directory data system, so they can be
|
||||
# opened nya
|
||||
for non_extended_path in randomised_brush_subdirectory_map[randomised_subdir]:
|
||||
full_paths.append(current_main_directory.plus_file(
|
||||
randomised_subdir
|
||||
).plus_file(
|
||||
non_extended_path
|
||||
))
|
||||
# Now load!
|
||||
add_randomised_brush(full_paths, randomised_subdir)
|
||||
# and mark that we are done in the overall map ^.^
|
||||
randomised_brush_subdirectories[randomised_subdir] = true
|
||||
# Now to iterate over the nonrandom brush files inside directories
|
||||
for nonrandomised_subdir in nonrandomised_brush_subdirectory_map:
|
||||
# initialise the set-map for this one if not already present :)
|
||||
if not (nonrandomised_subdir in processed_subdir_paths):
|
||||
processed_subdir_paths[nonrandomised_subdir] = {}
|
||||
# Get the paths within this subdirectory to check if they are
|
||||
# processed or not and if not, then process them.
|
||||
var relpaths_of_contained_nonrandom_brushes : Array = nonrandomised_brush_subdirectory_map[nonrandomised_subdir]
|
||||
for relative_path in relpaths_of_contained_nonrandom_brushes:
|
||||
if not (relative_path in processed_subdir_paths[nonrandomised_subdir]):
|
||||
# We are not yet processed
|
||||
var full_path : String = current_main_directory.plus_file(
|
||||
nonrandomised_subdir
|
||||
).plus_file(
|
||||
relative_path
|
||||
)
|
||||
# Add the path with the tooltip including the directory
|
||||
add_plain_brush(full_path, nonrandomised_subdir.plus_file(
|
||||
relative_path
|
||||
).get_basename())
|
||||
# Mark this as a processed relpath
|
||||
processed_subdir_paths[nonrandomised_subdir][relative_path] = true
|
||||
|
||||
Global.brushes_from_files = Global.custom_brushes.size()
|
||||
|
||||
|
||||
func import_patterns(priority_ordered_search_path: Array) -> void:
|
||||
for path in priority_ordered_search_path:
|
||||
var pattern_list := []
|
||||
var dir := Directory.new()
|
||||
dir.open(path)
|
||||
dir.list_dir_begin()
|
||||
var curr_file := dir.get_next()
|
||||
while curr_file != "":
|
||||
if curr_file.get_extension().to_lower() == "png":
|
||||
pattern_list.append(curr_file)
|
||||
curr_file = dir.get_next()
|
||||
dir.list_dir_end()
|
||||
|
||||
for pattern in pattern_list:
|
||||
var image := Image.new()
|
||||
var err := image.load(path.plus_file(pattern))
|
||||
if err == OK:
|
||||
image.convert(Image.FORMAT_RGBA8)
|
||||
Global.patterns.append(image)
|
||||
|
||||
var pattern_button : BaseButton = load("res://src/PatternButton.tscn").instance()
|
||||
pattern_button.image = image
|
||||
var pattern_tex := ImageTexture.new()
|
||||
pattern_tex.create_from_image(image, 0)
|
||||
pattern_button.get_child(0).texture = pattern_tex
|
||||
pattern_button.hint_tooltip = pattern
|
||||
pattern_button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND
|
||||
Global.patterns_popup.get_node("ScrollContainer/PatternContainer").add_child(pattern_button)
|
||||
|
||||
if Global.patterns.size() > 0:
|
||||
var image_size = Global.patterns[0].get_size()
|
||||
|
||||
Global.pattern_left_image = Global.patterns[0]
|
||||
var pattern_left_tex := ImageTexture.new()
|
||||
pattern_left_tex.create_from_image(Global.pattern_left_image, 0)
|
||||
Global.left_fill_pattern_container.get_child(0).get_child(0).texture = pattern_left_tex
|
||||
Global.left_fill_pattern_container.get_child(2).get_child(1).max_value = image_size.x - 1
|
||||
Global.left_fill_pattern_container.get_child(3).get_child(1).max_value = image_size.y - 1
|
||||
|
||||
Global.pattern_right_image = Global.patterns[0]
|
||||
var pattern_right_tex := ImageTexture.new()
|
||||
pattern_right_tex.create_from_image(Global.pattern_right_image, 0)
|
||||
Global.right_fill_pattern_container.get_child(0).get_child(0).texture = pattern_right_tex
|
||||
Global.right_fill_pattern_container.get_child(2).get_child(1).max_value = image_size.x - 1
|
||||
Global.right_fill_pattern_container.get_child(3).get_child(1).max_value = image_size.y - 1
|
||||
|
||||
|
||||
func import_gpl(path : String) -> Palette:
|
||||
var result : Palette = null
|
||||
var file = File.new()
|
||||
if file.file_exists(path):
|
||||
file.open(path, File.READ)
|
||||
var text = file.get_as_text()
|
||||
var lines = text.split('\n')
|
||||
var line_number := 0
|
||||
var comments := ""
|
||||
for line in lines:
|
||||
# Check if valid Gimp Palette Library file
|
||||
if line_number == 0:
|
||||
if line != "GIMP Palette":
|
||||
break
|
||||
else:
|
||||
result = Palette.new()
|
||||
var name_start = path.find_last('/') + 1
|
||||
var name_end = path.find_last('.')
|
||||
if name_end > name_start:
|
||||
result.name = path.substr(name_start, name_end - name_start)
|
||||
|
||||
# Comments
|
||||
if line.begins_with('#'):
|
||||
comments += line.trim_prefix('#') + '\n'
|
||||
pass
|
||||
elif line_number > 0 && line.length() >= 12:
|
||||
line = line.replace("\t", " ")
|
||||
var color_data : PoolStringArray = line.split(" ", false, 4)
|
||||
var red : float = color_data[0].to_float() / 255.0
|
||||
var green : float = color_data[1].to_float() / 255.0
|
||||
var blue : float = color_data[2].to_float() / 255.0
|
||||
var color = Color(red, green, blue)
|
||||
result.add_color(color, color_data[3])
|
||||
line_number += 1
|
||||
|
||||
if result:
|
||||
result.comments = comments
|
||||
file.close()
|
||||
|
||||
return result
|
||||
|
||||
|
||||
func import_png_palette(path: String) -> Palette:
|
||||
var result: Palette = null
|
||||
|
||||
var image := Image.new()
|
||||
var err := image.load(path)
|
||||
if err != OK: # An error occured
|
||||
return null
|
||||
|
||||
var height: int = image.get_height()
|
||||
var width: int = image.get_width()
|
||||
|
||||
result = Palette.new()
|
||||
|
||||
# Iterate all pixels and store unique colors to palete
|
||||
image.lock()
|
||||
for y in range(0, height):
|
||||
for x in range(0, width):
|
||||
var color: Color = image.get_pixel(x, y)
|
||||
if not result.has_color(color):
|
||||
result.add_color(color, "#" + color.to_html())
|
||||
image.unlock()
|
||||
|
||||
var name_start = path.find_last('/') + 1
|
||||
var name_end = path.find_last('.')
|
||||
if name_end > name_start:
|
||||
result.name = path.substr(name_start, name_end - name_start)
|
||||
|
||||
return result
|
313
src/Autoload/OpenSave.gd
Normal file
313
src/Autoload/OpenSave.gd
Normal file
|
@ -0,0 +1,313 @@
|
|||
extends Node
|
||||
|
||||
var current_save_path := ""
|
||||
# Stores a filename of a backup file in user:// until user saves manually
|
||||
var backup_save_path = ""
|
||||
var default_autosave_interval := 5 # Minutes
|
||||
|
||||
onready var autosave_timer : Timer
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
autosave_timer = Timer.new()
|
||||
autosave_timer.one_shot = false
|
||||
autosave_timer.process_mode = Timer.TIMER_PROCESS_IDLE
|
||||
autosave_timer.connect("timeout", self, "_on_Autosave_timeout")
|
||||
add_child(autosave_timer)
|
||||
set_autosave_interval(default_autosave_interval)
|
||||
toggle_autosave(false) # Gets started from preferences dialog
|
||||
|
||||
|
||||
func open_pxo_file(path : String, untitled_backup : bool = false) -> void:
|
||||
var file := File.new()
|
||||
var err := file.open_compressed(path, File.READ, File.COMPRESSION_ZSTD)
|
||||
if err == ERR_FILE_UNRECOGNIZED:
|
||||
err = file.open(path, File.READ) # If the file is not compressed open it raw (pre-v0.7)
|
||||
|
||||
if err != OK:
|
||||
Global.notification_label("File failed to open")
|
||||
file.close()
|
||||
return
|
||||
|
||||
var file_version := file.get_line() # Example, "v0.6"
|
||||
var file_major_version = int(file_version.substr(1, 1))
|
||||
var file_minor_version = int(file_version.substr(3, 1))
|
||||
|
||||
if file_major_version == 0 and file_minor_version < 5:
|
||||
Global.notification_label("File is from an older version of Pixelorama, as such it might not work properly")
|
||||
|
||||
var frame := 0
|
||||
Global.layers.clear()
|
||||
if file_major_version >= 0 and file_minor_version > 6:
|
||||
var global_layer_line := file.get_line()
|
||||
while global_layer_line == ".":
|
||||
var layer_name := file.get_line()
|
||||
var layer_visibility := file.get_8()
|
||||
var layer_lock := file.get_8()
|
||||
var layer_new_frames_linked := file.get_8()
|
||||
var linked_frames = file.get_var()
|
||||
|
||||
# Store [Layer name (0), Layer visibility boolean (1), Layer lock boolean (2), Frame container (3),
|
||||
# will new frames be linked boolean (4), Array of linked frames (5)]
|
||||
Global.layers.append([layer_name, layer_visibility, layer_lock, HBoxContainer.new(), layer_new_frames_linked, linked_frames])
|
||||
global_layer_line = file.get_line()
|
||||
|
||||
var frame_line := file.get_line()
|
||||
Global.clear_canvases()
|
||||
while frame_line == "--": # Load frames
|
||||
var canvas : Canvas = load("res://src/Canvas.tscn").instance()
|
||||
Global.canvas = canvas
|
||||
var width := file.get_16()
|
||||
var height := file.get_16()
|
||||
|
||||
var layer_line := file.get_line()
|
||||
while layer_line == "-": # Load layers
|
||||
var buffer := file.get_buffer(width * height * 4)
|
||||
if file_major_version == 0 and file_minor_version < 7:
|
||||
var layer_name_old_version = file.get_line()
|
||||
if frame == 0:
|
||||
# Store [Layer name (0), Layer visibility boolean (1), Layer lock boolean (2), Frame container (3),
|
||||
# will new frames be linked boolean (4), Array of linked frames (5)]
|
||||
Global.layers.append([layer_name_old_version, true, false, HBoxContainer.new(), false, []])
|
||||
var layer_transparency := 1.0
|
||||
if file_major_version >= 0 and file_minor_version > 5:
|
||||
layer_transparency = file.get_float()
|
||||
var image := Image.new()
|
||||
image.create_from_data(width, height, false, Image.FORMAT_RGBA8, buffer)
|
||||
image.lock()
|
||||
var tex := ImageTexture.new()
|
||||
tex.create_from_image(image, 0)
|
||||
canvas.layers.append([image, tex, layer_transparency])
|
||||
layer_line = file.get_line()
|
||||
|
||||
var guide_line := file.get_line() # "guideline" no pun intended
|
||||
while guide_line == "|": # Load guides
|
||||
var guide := Guide.new()
|
||||
guide.default_color = Color.purple
|
||||
guide.type = file.get_8()
|
||||
if guide.type == guide.Types.HORIZONTAL:
|
||||
guide.add_point(Vector2(-99999, file.get_16()))
|
||||
guide.add_point(Vector2(99999, file.get_16()))
|
||||
else:
|
||||
guide.add_point(Vector2(file.get_16(), -99999))
|
||||
guide.add_point(Vector2(file.get_16(), 99999))
|
||||
guide.has_focus = false
|
||||
canvas.add_child(guide)
|
||||
guide_line = file.get_line()
|
||||
|
||||
canvas.size = Vector2(width, height)
|
||||
Global.canvases.append(canvas)
|
||||
canvas.frame = frame
|
||||
Global.canvas_parent.add_child(canvas)
|
||||
frame_line = file.get_line()
|
||||
frame += 1
|
||||
|
||||
Global.canvases = Global.canvases # Just to call Global.canvases_changed
|
||||
Global.current_frame = frame - 1
|
||||
Global.layers = Global.layers # Just to call Global.layers_changed
|
||||
# Load tool options
|
||||
Global.left_color_picker.color = file.get_var()
|
||||
Global.right_color_picker.color = file.get_var()
|
||||
Global.left_brush_size = file.get_8()
|
||||
Global.left_brush_size_edit.value = Global.left_brush_size
|
||||
Global.right_brush_size = file.get_8()
|
||||
Global.right_brush_size_edit.value = Global.right_brush_size
|
||||
if file_major_version == 0 and file_minor_version < 7:
|
||||
var left_palette = file.get_var()
|
||||
var right_palette = file.get_var()
|
||||
for color in left_palette:
|
||||
Global.left_color_picker.get_picker().add_preset(color)
|
||||
for color in right_palette:
|
||||
Global.right_color_picker.get_picker().add_preset(color)
|
||||
|
||||
# Load custom brushes
|
||||
Global.custom_brushes.resize(Global.brushes_from_files)
|
||||
Global.remove_brush_buttons()
|
||||
|
||||
var brush_line := file.get_line()
|
||||
while brush_line == "/":
|
||||
var b_width := file.get_16()
|
||||
var b_height := file.get_16()
|
||||
var buffer := file.get_buffer(b_width * b_height * 4)
|
||||
var image := Image.new()
|
||||
image.create_from_data(b_width, b_height, false, Image.FORMAT_RGBA8, buffer)
|
||||
Global.custom_brushes.append(image)
|
||||
Global.create_brush_button(image)
|
||||
brush_line = file.get_line()
|
||||
|
||||
if file_major_version >= 0 and file_minor_version > 6:
|
||||
var tag_line := file.get_line()
|
||||
while tag_line == ".T/":
|
||||
var tag_name := file.get_line()
|
||||
var tag_color : Color = file.get_var()
|
||||
var tag_from := file.get_8()
|
||||
var tag_to := file.get_8()
|
||||
Global.animation_tags.append([tag_name, tag_color, tag_from, tag_to])
|
||||
Global.animation_tags = Global.animation_tags # To execute animation_tags_changed()
|
||||
tag_line = file.get_line()
|
||||
|
||||
file.close()
|
||||
|
||||
if not untitled_backup:
|
||||
# Untitled backup should not change window title and save path
|
||||
current_save_path = path
|
||||
Global.window_title = path.get_file() + " - Pixelorama"
|
||||
Global.project_has_changed = false
|
||||
|
||||
|
||||
func save_pxo_file(path : String, autosave : bool) -> void:
|
||||
var file := File.new()
|
||||
var err := file.open_compressed(path, File.WRITE, File.COMPRESSION_ZSTD)
|
||||
if err == OK:
|
||||
# Store Pixelorama version
|
||||
file.store_line(ProjectSettings.get_setting("application/config/Version"))
|
||||
|
||||
# Store Global layers
|
||||
for layer in Global.layers:
|
||||
file.store_line(".")
|
||||
file.store_line(layer[0]) # Layer name
|
||||
file.store_8(layer[1]) # Layer visibility
|
||||
file.store_8(layer[2]) # Layer lock
|
||||
file.store_8(layer[4]) # Future frames linked
|
||||
file.store_var(layer[5]) # Linked frames
|
||||
file.store_line("END_GLOBAL_LAYERS")
|
||||
|
||||
# Store frames
|
||||
for canvas in Global.canvases:
|
||||
file.store_line("--")
|
||||
file.store_16(canvas.size.x)
|
||||
file.store_16(canvas.size.y)
|
||||
for layer in canvas.layers: # Store canvas layers
|
||||
file.store_line("-")
|
||||
file.store_buffer(layer[0].get_data())
|
||||
file.store_float(layer[2]) # Layer transparency
|
||||
file.store_line("END_LAYERS")
|
||||
|
||||
# Store guides
|
||||
for child in canvas.get_children():
|
||||
if child is Guide:
|
||||
file.store_line("|")
|
||||
file.store_8(child.type)
|
||||
if child.type == child.Types.HORIZONTAL:
|
||||
file.store_16(child.points[0].y)
|
||||
file.store_16(child.points[1].y)
|
||||
else:
|
||||
file.store_16(child.points[1].x)
|
||||
file.store_16(child.points[0].x)
|
||||
file.store_line("END_GUIDES")
|
||||
file.store_line("END_FRAMES")
|
||||
|
||||
# Save tool options
|
||||
var left_color : Color = Global.left_color_picker.color
|
||||
var right_color : Color = Global.right_color_picker.color
|
||||
var left_brush_size : int = Global.left_brush_size
|
||||
var right_brush_size : int = Global.right_brush_size
|
||||
file.store_var(left_color)
|
||||
file.store_var(right_color)
|
||||
file.store_8(left_brush_size)
|
||||
file.store_8(right_brush_size)
|
||||
|
||||
# Save custom brushes
|
||||
for i in range(Global.brushes_from_files, Global.custom_brushes.size()):
|
||||
var brush = Global.custom_brushes[i]
|
||||
file.store_line("/")
|
||||
file.store_16(brush.get_size().x)
|
||||
file.store_16(brush.get_size().y)
|
||||
file.store_buffer(brush.get_data())
|
||||
file.store_line("END_BRUSHES")
|
||||
|
||||
# Store animation tags
|
||||
for tag in Global.animation_tags:
|
||||
file.store_line(".T/")
|
||||
file.store_line(tag[0]) # Tag name
|
||||
file.store_var(tag[1]) # Tag color
|
||||
file.store_8(tag[2]) # Tag "from", the first frame
|
||||
file.store_8(tag[3]) # Tag "to", the last frame
|
||||
file.store_line("END_FRAME_TAGS")
|
||||
|
||||
file.close()
|
||||
|
||||
if Global.project_has_changed and not autosave:
|
||||
Global.project_has_changed = false
|
||||
|
||||
if autosave:
|
||||
Global.notification_label("File autosaved")
|
||||
else:
|
||||
# First remove backup then set current save path
|
||||
remove_backup()
|
||||
current_save_path = path
|
||||
Global.notification_label("File saved")
|
||||
|
||||
if backup_save_path == "":
|
||||
Global.window_title = path.get_file() + " - Pixelorama"
|
||||
|
||||
else:
|
||||
Global.notification_label("File failed to save")
|
||||
|
||||
|
||||
func toggle_autosave(enable : bool) -> void:
|
||||
if enable:
|
||||
autosave_timer.start()
|
||||
else:
|
||||
autosave_timer.stop()
|
||||
|
||||
|
||||
func set_autosave_interval(interval : float) -> void:
|
||||
autosave_timer.wait_time = interval * 60 # Interval parameter is in minutes, wait_time is seconds
|
||||
autosave_timer.start()
|
||||
|
||||
|
||||
func _on_Autosave_timeout() -> void:
|
||||
if backup_save_path == "":
|
||||
# Create a new backup file if it doesn't exist yet
|
||||
backup_save_path = "user://backup-" + String(OS.get_unix_time())
|
||||
|
||||
store_backup_path()
|
||||
save_pxo_file(backup_save_path, true)
|
||||
|
||||
|
||||
# Backup paths are stored in two ways:
|
||||
# 1) User already manually saved and defined a save path -> {current_save_path, backup_save_path}
|
||||
# 2) User didn't manually saved, "untitled" backup is stored -> {backup_save_path, backup_save_path}
|
||||
func store_backup_path() -> void:
|
||||
if current_save_path != "":
|
||||
# Remove "untitled" backup if it existed on this project instance
|
||||
if Global.config_cache.has_section_key("backups", backup_save_path):
|
||||
Global.config_cache.erase_section_key("backups", backup_save_path)
|
||||
|
||||
Global.config_cache.set_value("backups", current_save_path, backup_save_path)
|
||||
else:
|
||||
Global.config_cache.set_value("backups", backup_save_path, backup_save_path)
|
||||
|
||||
Global.config_cache.save("user://cache.ini")
|
||||
|
||||
|
||||
func remove_backup() -> void:
|
||||
# Remove backup file
|
||||
if backup_save_path != "":
|
||||
if current_save_path != "":
|
||||
remove_backup_by_path(current_save_path, backup_save_path)
|
||||
else:
|
||||
# If manual save was not yet done - remove "untitled" backup
|
||||
remove_backup_by_path(backup_save_path, backup_save_path)
|
||||
backup_save_path = ""
|
||||
|
||||
|
||||
func remove_backup_by_path(project_path : String, backup_path : String) -> void:
|
||||
Directory.new().remove(backup_path)
|
||||
Global.config_cache.erase_section_key("backups", project_path)
|
||||
Global.config_cache.save("user://cache.ini")
|
||||
|
||||
|
||||
func reload_backup_file(project_path : String, backup_path : String) -> void:
|
||||
# If project path is the same as backup save path -> the backup was untitled
|
||||
open_pxo_file(backup_path, project_path == backup_path)
|
||||
backup_save_path = backup_path
|
||||
|
||||
if project_path != backup_path:
|
||||
current_save_path = project_path
|
||||
Global.window_title = project_path.get_file() + " - Pixelorama(*)"
|
||||
Global.project_has_changed = true
|
||||
|
||||
Global.notification_label("Backup reloaded")
|
||||
|
108
src/BrushButton.gd
Normal file
108
src/BrushButton.gd
Normal file
|
@ -0,0 +1,108 @@
|
|||
extends BaseButton
|
||||
|
||||
|
||||
signal brush_selected
|
||||
|
||||
export var brush_type = 0 # Global.Brush_Types.PIXEL
|
||||
export var custom_brush_index := -3
|
||||
var random_brushes := []
|
||||
|
||||
|
||||
func _on_BrushButton_pressed() -> void:
|
||||
# Delete the brush on middle mouse press
|
||||
if Input.is_action_just_released("middle_mouse"):
|
||||
_on_DeleteButton_pressed()
|
||||
return
|
||||
|
||||
# Change left brush
|
||||
if Global.brush_type_window_position == "left":
|
||||
Global.current_left_brush_type = brush_type
|
||||
Global.custom_left_brush_index = custom_brush_index
|
||||
if custom_brush_index > -1: # Custom brush
|
||||
if Global.current_left_tool == "Pencil":
|
||||
Global.left_color_interpolation_container.visible = true
|
||||
# if hint_tooltip == "":
|
||||
# Global.left_brush_type_label.text = tr("Custom brush")
|
||||
# else:
|
||||
# Global.left_brush_type_label.text = tr("Brush:") + " %s" % hint_tooltip
|
||||
elif custom_brush_index == -3: # Pixel brush
|
||||
Global.left_color_interpolation_container.visible = false
|
||||
# Global.left_brush_type_label.text = tr("Brush: Pixel")
|
||||
elif custom_brush_index == -2: # Circle brush
|
||||
Global.left_color_interpolation_container.visible = false
|
||||
# Global.left_brush_type_label.text = tr("Brush: Circle")
|
||||
elif custom_brush_index == -1: # Filled Circle brush
|
||||
Global.left_color_interpolation_container.visible = false
|
||||
# Global.left_brush_type_label.text = tr("Brush: Filled Circle")
|
||||
|
||||
Global.update_left_custom_brush()
|
||||
emit_signal("brush_selected")
|
||||
|
||||
else: # Change right brush
|
||||
Global.current_right_brush_type = brush_type
|
||||
Global.custom_right_brush_index = custom_brush_index
|
||||
if custom_brush_index > -1:
|
||||
if Global.current_right_tool == "Pencil":
|
||||
Global.right_color_interpolation_container.visible = true
|
||||
# if hint_tooltip == "":
|
||||
# Global.right_brush_type_label.text = tr("Custom brush")
|
||||
# else:
|
||||
# Global.right_brush_type_label.text = tr("Brush:") + " %s" % hint_tooltip
|
||||
elif custom_brush_index == -3: # Pixel brush
|
||||
Global.right_color_interpolation_container.visible = false
|
||||
# Global.right_brush_type_label.text = tr("Brush: Pixel")
|
||||
elif custom_brush_index == -2: # Circle brush
|
||||
Global.right_color_interpolation_container.visible = false
|
||||
# Global.right_brush_type_label.text = tr("Brush: Circle")
|
||||
elif custom_brush_index == -1: # Filled Circle brush
|
||||
Global.right_color_interpolation_container.visible = false
|
||||
# Global.right_brush_type_label.text = tr("Brush: Filled Circle")
|
||||
|
||||
Global.update_right_custom_brush()
|
||||
emit_signal("brush_selected")
|
||||
|
||||
|
||||
func _on_DeleteButton_pressed() -> void:
|
||||
if brush_type == Global.Brush_Types.CUSTOM:
|
||||
if Global.custom_left_brush_index == custom_brush_index:
|
||||
Global.custom_left_brush_index = -3
|
||||
Global.current_left_brush_type = Global.Brush_Types.PIXEL
|
||||
# Global.left_brush_type_label.text = "Brush: Pixel"
|
||||
Global.update_left_custom_brush()
|
||||
if Global.custom_right_brush_index == custom_brush_index:
|
||||
Global.custom_right_brush_index = -3
|
||||
Global.current_right_brush_type = Global.Brush_Types.PIXEL
|
||||
# Global.right_brush_type_label.text = "Brush: Pixel"
|
||||
Global.update_right_custom_brush()
|
||||
|
||||
var project_brush_index = custom_brush_index - Global.brushes_from_files
|
||||
Global.undos += 1
|
||||
Global.undo_redo.create_action("Delete Custom Brush")
|
||||
for i in range(project_brush_index, Global.project_brush_container.get_child_count()):
|
||||
var bb = Global.project_brush_container.get_child(i)
|
||||
if Global.custom_left_brush_index == bb.custom_brush_index:
|
||||
Global.custom_left_brush_index -= 1
|
||||
if Global.custom_right_brush_index == bb.custom_brush_index:
|
||||
Global.custom_right_brush_index -= 1
|
||||
|
||||
Global.undo_redo.add_do_property(bb, "custom_brush_index", bb.custom_brush_index - 1)
|
||||
Global.undo_redo.add_undo_property(bb, "custom_brush_index", bb.custom_brush_index)
|
||||
|
||||
var custom_brushes: Array = Global.custom_brushes.duplicate()
|
||||
custom_brushes.remove(custom_brush_index)
|
||||
|
||||
Global.undo_redo.add_do_property(Global, "custom_brushes", custom_brushes)
|
||||
Global.undo_redo.add_undo_property(Global, "custom_brushes", Global.custom_brushes)
|
||||
Global.undo_redo.add_do_method(Global, "redo_custom_brush", self)
|
||||
Global.undo_redo.add_undo_method(Global, "undo_custom_brush", self)
|
||||
Global.undo_redo.commit_action()
|
||||
|
||||
|
||||
func _on_BrushButton_mouse_entered() -> void:
|
||||
if brush_type == Global.Brush_Types.CUSTOM:
|
||||
$DeleteButton.visible = true
|
||||
|
||||
|
||||
func _on_BrushButton_mouse_exited() -> void:
|
||||
if brush_type == Global.Brush_Types.CUSTOM:
|
||||
$DeleteButton.visible = false
|
58
src/BrushButton.tscn
Normal file
58
src/BrushButton.tscn
Normal file
File diff suppressed because one or more lines are too long
161
src/CameraMovement.gd
Normal file
161
src/CameraMovement.gd
Normal file
|
@ -0,0 +1,161 @@
|
|||
extends Camera2D
|
||||
|
||||
|
||||
var tween : Tween
|
||||
var zoom_min := Vector2(0.005, 0.005)
|
||||
var zoom_max := Vector2.ONE
|
||||
var viewport_container : ViewportContainer
|
||||
var mouse_pos := Vector2.ZERO
|
||||
var drag := false
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
viewport_container = get_parent().get_parent()
|
||||
tween = Tween.new()
|
||||
add_child(tween)
|
||||
tween.connect("tween_step", self, "_on_tween_step")
|
||||
|
||||
|
||||
# Get the speed multiplier for when you've pressed
|
||||
# a movement key for the given amount of time
|
||||
func dir_move_zoom_multiplier(press_time : float) -> float:
|
||||
if press_time < 0:
|
||||
return 0.0
|
||||
if Input.is_key_pressed(KEY_SHIFT) and Input.is_key_pressed(KEY_CONTROL) :
|
||||
return Global.high_speed_move_rate
|
||||
elif Input.is_key_pressed(KEY_SHIFT):
|
||||
return Global.medium_speed_move_rate
|
||||
elif !Input.is_key_pressed(KEY_CONTROL):
|
||||
# control + right/left is used to move frames so
|
||||
# we do this check to ensure that there is no conflict
|
||||
return Global.low_speed_move_rate
|
||||
else:
|
||||
return 0.0
|
||||
|
||||
|
||||
func reset_dir_move_time(direction) -> void:
|
||||
Global.key_move_press_time[direction] = 0.0
|
||||
|
||||
|
||||
const key_move_action_names := ["ui_up", "ui_down", "ui_left", "ui_right"]
|
||||
|
||||
# Check if an event is a ui_up/down/left/right event-press :)
|
||||
func is_action_direction_pressed(event : InputEvent, allow_echo: bool = true) -> bool:
|
||||
for action in key_move_action_names:
|
||||
if event.is_action_pressed(action, allow_echo):
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
# Check if an event is a ui_up/down/left/right event release nya
|
||||
func is_action_direction_released(event: InputEvent) -> bool:
|
||||
for action in key_move_action_names:
|
||||
if event.is_action_released(action):
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
# get the Direction associated with the event.
|
||||
# if not a direction event return null
|
||||
func get_action_direction(event: InputEvent): # -> Optional[Direction]
|
||||
if event.is_action("ui_up"):
|
||||
return Global.Direction.UP
|
||||
elif event.is_action("ui_down"):
|
||||
return Global.Direction.DOWN
|
||||
elif event.is_action("ui_left"):
|
||||
return Global.Direction.LEFT
|
||||
elif event.is_action("ui_right"):
|
||||
return Global.Direction.RIGHT
|
||||
return null
|
||||
|
||||
|
||||
# Holds sign multipliers for the given directions nyaa
|
||||
# (per the indices in Global.gd defined by Direction)
|
||||
# UP, DOWN, LEFT, RIGHT in that order
|
||||
const directional_sign_multipliers := [
|
||||
Vector2(0.0, -1.0),
|
||||
Vector2(0.0, 1.0),
|
||||
Vector2(-1.0, 0.0),
|
||||
Vector2(1.0, 0.0)
|
||||
]
|
||||
|
||||
|
||||
# Process an action event for a pressed direction
|
||||
# action
|
||||
func process_direction_action_pressed(event: InputEvent) -> void:
|
||||
var dir = get_action_direction(event)
|
||||
if dir == null:
|
||||
return
|
||||
var increment := get_process_delta_time()
|
||||
# Count the total time we've been doing this ^.^
|
||||
Global.key_move_press_time[dir] += increment
|
||||
var this_direction_press_time : float = Global.key_move_press_time[dir]
|
||||
var move_speed := dir_move_zoom_multiplier(this_direction_press_time)
|
||||
offset = offset + move_speed * increment * directional_sign_multipliers[dir] * zoom
|
||||
|
||||
|
||||
# Process an action for a release direction action
|
||||
func process_direction_action_released(event: InputEvent) -> void:
|
||||
var dir = get_action_direction(event)
|
||||
if dir == null:
|
||||
return
|
||||
reset_dir_move_time(dir)
|
||||
|
||||
|
||||
func _input(event : InputEvent) -> void:
|
||||
mouse_pos = viewport_container.get_local_mouse_position()
|
||||
var viewport_size := viewport_container.rect_size
|
||||
if event.is_action_pressed("middle_mouse") || event.is_action_pressed("space"):
|
||||
drag = true
|
||||
elif event.is_action_released("middle_mouse") || event.is_action_released("space"):
|
||||
drag = false
|
||||
|
||||
if Global.can_draw && Rect2(Vector2.ZERO, viewport_size).has_point(mouse_pos):
|
||||
if event.is_action_pressed("zoom_in"): # Wheel Up Event
|
||||
zoom_camera(-1)
|
||||
elif event.is_action_pressed("zoom_out"): # Wheel Down Event
|
||||
zoom_camera(1)
|
||||
elif event is InputEventMouseMotion && drag:
|
||||
offset = offset - event.relative * zoom
|
||||
elif is_action_direction_pressed(event):
|
||||
process_direction_action_pressed(event)
|
||||
elif is_action_direction_released(event):
|
||||
process_direction_action_released(event)
|
||||
|
||||
Global.horizontal_ruler.update()
|
||||
Global.vertical_ruler.update()
|
||||
|
||||
|
||||
# Zoom Camera
|
||||
func zoom_camera(dir : int) -> void:
|
||||
var viewport_size := viewport_container.rect_size
|
||||
if Global.smooth_zoom:
|
||||
var zoom_margin = zoom * dir / 5
|
||||
var new_zoom = zoom + zoom_margin
|
||||
if new_zoom > zoom_min && new_zoom < zoom_max:
|
||||
var new_offset = offset + (-0.5 * viewport_size + mouse_pos) * (zoom - new_zoom)
|
||||
tween.interpolate_property(self, "zoom", zoom, new_zoom, 0.05, Tween.TRANS_LINEAR, Tween.EASE_IN)
|
||||
tween.interpolate_property(self, "offset", offset, new_offset, 0.05, Tween.TRANS_LINEAR, Tween.EASE_IN)
|
||||
tween.start()
|
||||
|
||||
if name == "Camera2D":
|
||||
Global.zoom_level_label.text = str(round(100 / new_zoom.x)) + " %"
|
||||
|
||||
else:
|
||||
var prev_zoom := zoom
|
||||
var zoom_margin = zoom * dir / 10
|
||||
if zoom + zoom_margin > zoom_min:
|
||||
zoom += zoom_margin
|
||||
|
||||
if zoom > zoom_max:
|
||||
zoom = zoom_max
|
||||
|
||||
offset = offset + (-0.5 * viewport_size + mouse_pos) * (prev_zoom - zoom)
|
||||
if name == "Camera2D":
|
||||
Global.zoom_level_label.text = str(round(100 / Global.camera.zoom.x)) + " %"
|
||||
|
||||
|
||||
|
||||
func _on_tween_step(_object: Object, _key: NodePath, _elapsed: float, _value: Object) -> void:
|
||||
Global.horizontal_ruler.update()
|
||||
Global.vertical_ruler.update()
|
1104
src/Canvas.gd
Normal file
1104
src/Canvas.gd
Normal file
File diff suppressed because it is too large
Load diff
7
src/Canvas.tscn
Normal file
7
src/Canvas.tscn
Normal file
|
@ -0,0 +1,7 @@
|
|||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://src/Canvas.gd" type="Script" id=1]
|
||||
|
||||
|
||||
[node name="Canvas" type="Node2D"]
|
||||
script = ExtResource( 1 )
|
121
src/CelButton.gd
Normal file
121
src/CelButton.gd
Normal file
|
@ -0,0 +1,121 @@
|
|||
extends Button
|
||||
|
||||
var frame := 0
|
||||
var layer := 0
|
||||
|
||||
onready var popup_menu : PopupMenu = $PopupMenu
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
hint_tooltip = "Frame: %s, Layer: %s" % [frame + 1, layer]
|
||||
if Global.canvases[frame] in Global.layers[layer][5]:
|
||||
get_node("LinkedIndicator").visible = true
|
||||
popup_menu.set_item_text(4, "Unlink Cel")
|
||||
popup_menu.set_item_metadata(4, "Unlink Cel")
|
||||
else:
|
||||
get_node("LinkedIndicator").visible = false
|
||||
popup_menu.set_item_text(4, "Link Cel")
|
||||
popup_menu.set_item_metadata(4, "Link Cel")
|
||||
|
||||
|
||||
func _on_CelButton_pressed() -> void:
|
||||
if Input.is_action_just_released("left_mouse"):
|
||||
Global.current_frame = frame
|
||||
Global.current_layer = layer
|
||||
elif Input.is_action_just_released("right_mouse"):
|
||||
if Global.canvases.size() == 1:
|
||||
popup_menu.set_item_disabled(0, true)
|
||||
popup_menu.set_item_disabled(2, true)
|
||||
popup_menu.set_item_disabled(3, true)
|
||||
else:
|
||||
popup_menu.set_item_disabled(0, false)
|
||||
if frame > 0:
|
||||
popup_menu.set_item_disabled(2, false)
|
||||
if frame < Global.canvases.size() - 1:
|
||||
popup_menu.set_item_disabled(3, false)
|
||||
popup_menu.popup(Rect2(get_global_mouse_position(), Vector2.ONE))
|
||||
pressed = !pressed
|
||||
elif Input.is_action_just_released("middle_mouse"): # Middle mouse click
|
||||
pressed = !pressed
|
||||
Global.animation_timeline._on_DeleteFrame_pressed(frame)
|
||||
else: # An example of this would be Space
|
||||
pressed = !pressed
|
||||
|
||||
|
||||
func _on_PopupMenu_id_pressed(ID : int) -> void:
|
||||
match ID:
|
||||
0: # Remove Frame
|
||||
Global.animation_timeline._on_DeleteFrame_pressed(frame)
|
||||
1: # Clone Frame
|
||||
Global.animation_timeline._on_CopyFrame_pressed(frame)
|
||||
2: # Move Left
|
||||
change_frame_order(-1)
|
||||
3: # Move Right
|
||||
change_frame_order(1)
|
||||
4: # Unlink Cel
|
||||
var cel_index : int = Global.layers[layer][5].find(Global.canvases[frame])
|
||||
var c = Global.canvases[frame]
|
||||
var new_layers := Global.layers.duplicate(true)
|
||||
var new_canvas_layers : Array = c.layers.duplicate(true)
|
||||
|
||||
if popup_menu.get_item_metadata(4) == "Unlink Cel":
|
||||
new_layers[layer][5].remove(cel_index)
|
||||
var sprite := Image.new()
|
||||
sprite.copy_from(Global.canvases[frame].layers[layer][0])
|
||||
sprite.lock()
|
||||
var tex := ImageTexture.new()
|
||||
tex.create_from_image(sprite, 0)
|
||||
new_canvas_layers[layer][0] = sprite
|
||||
new_canvas_layers[layer][1] = tex
|
||||
|
||||
Global.undo_redo.create_action("Unlink Cel")
|
||||
Global.undo_redo.add_do_property(Global, "layers", new_layers)
|
||||
Global.undo_redo.add_do_property(c, "layers", new_canvas_layers)
|
||||
Global.undo_redo.add_undo_property(Global, "layers", Global.layers)
|
||||
Global.undo_redo.add_undo_property(c, "layers", c.layers)
|
||||
|
||||
Global.undo_redo.add_undo_method(Global, "undo", [Global.canvases[frame]], layer)
|
||||
Global.undo_redo.add_do_method(Global, "redo", [Global.canvases[frame]], layer)
|
||||
Global.undo_redo.commit_action()
|
||||
elif popup_menu.get_item_metadata(4) == "Link Cel":
|
||||
new_layers[layer][5].append(Global.canvases[frame])
|
||||
Global.undo_redo.create_action("Link Cel")
|
||||
Global.undo_redo.add_do_property(Global, "layers", new_layers)
|
||||
if new_layers[layer][5].size() > 1:
|
||||
# If there are already linked cels, set the current cel's image
|
||||
# to the first linked cel's image
|
||||
new_canvas_layers[layer][0] = new_layers[layer][5][0].layers[layer][0]
|
||||
new_canvas_layers[layer][1] = new_layers[layer][5][0].layers[layer][1]
|
||||
Global.undo_redo.add_do_property(c, "layers", new_canvas_layers)
|
||||
Global.undo_redo.add_undo_property(c, "layers", c.layers)
|
||||
|
||||
Global.undo_redo.add_undo_property(Global, "layers", Global.layers)
|
||||
Global.undo_redo.add_undo_method(Global, "undo", [Global.canvases[frame]], layer)
|
||||
Global.undo_redo.add_do_method(Global, "redo", [Global.canvases[frame]], layer)
|
||||
Global.undo_redo.commit_action()
|
||||
|
||||
|
||||
func change_frame_order(rate : int) -> void:
|
||||
var change = frame + rate
|
||||
var new_canvases := Global.canvases.duplicate()
|
||||
var temp = new_canvases[frame]
|
||||
new_canvases[frame] = new_canvases[change]
|
||||
new_canvases[change] = temp
|
||||
|
||||
Global.undo_redo.create_action("Change Frame Order")
|
||||
Global.undo_redo.add_do_property(Global, "canvases", new_canvases)
|
||||
Global.undo_redo.add_do_property(Global.canvases[frame], "frame", change)
|
||||
Global.undo_redo.add_do_property(Global.canvases[change], "frame", frame)
|
||||
|
||||
if Global.current_frame == frame:
|
||||
Global.undo_redo.add_do_property(Global, "current_frame", change)
|
||||
Global.undo_redo.add_undo_property(Global, "current_frame", Global.current_frame)
|
||||
|
||||
Global.undo_redo.add_undo_property(Global, "canvases", Global.canvases)
|
||||
Global.undo_redo.add_undo_property(Global.canvases[frame], "frame", frame)
|
||||
Global.undo_redo.add_undo_property(Global.canvases[change], "frame", change)
|
||||
|
||||
Global.undo_redo.add_undo_method(Global, "undo", [Global.canvases[frame]])
|
||||
Global.undo_redo.add_do_method(Global, "redo", [Global.canvases[frame]])
|
||||
Global.undo_redo.commit_action()
|
||||
|
47
src/CelButton.tscn
Normal file
47
src/CelButton.tscn
Normal file
|
@ -0,0 +1,47 @@
|
|||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://src/CelButton.gd" type="Script" id=1]
|
||||
|
||||
[node name="CelButton" type="Button"]
|
||||
margin_top = 18.0
|
||||
margin_right = 36.0
|
||||
margin_bottom = 54.0
|
||||
rect_min_size = Vector2( 36, 36 )
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 0
|
||||
size_flags_vertical = 0
|
||||
toggle_mode = true
|
||||
button_mask = 7
|
||||
script = ExtResource( 1 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="CelTexture" type="TextureRect" parent="."]
|
||||
margin_left = 2.0
|
||||
margin_top = 1.78536
|
||||
margin_right = 34.0
|
||||
margin_bottom = 33.7854
|
||||
rect_min_size = Vector2( 32, 32 )
|
||||
size_flags_horizontal = 0
|
||||
size_flags_vertical = 0
|
||||
expand = true
|
||||
stretch_mode = 6
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="PopupMenu" type="PopupMenu" parent="."]
|
||||
margin_right = 20.0
|
||||
margin_bottom = 20.0
|
||||
mouse_default_cursor_shape = 2
|
||||
items = [ "Remove Frame", null, 0, false, true, -1, 0, null, "", false, "Clone Frame", null, 0, false, false, -1, 0, null, "", false, "Move Left", null, 0, false, true, -1, 0, null, "", false, "Move Right", null, 0, false, true, -1, 0, null, "", false, "Link Cel", null, 0, false, false, -1, 0, null, "", false ]
|
||||
|
||||
[node name="LinkedIndicator" type="Polygon2D" parent="."]
|
||||
visible = false
|
||||
color = Color( 0.0627451, 0.741176, 0.215686, 1 )
|
||||
invert_enable = true
|
||||
invert_border = 1.0
|
||||
polygon = PoolVector2Array( 0, 0, 36, 0, 36, 36, 0, 36 )
|
||||
[connection signal="pressed" from="." to="." method="_on_CelButton_pressed"]
|
||||
[connection signal="id_pressed" from="PopupMenu" to="." method="_on_PopupMenu_id_pressed"]
|
122
src/Dialogs/AboutDialog.gd
Normal file
122
src/Dialogs/AboutDialog.gd
Normal file
|
@ -0,0 +1,122 @@
|
|||
extends WindowDialog
|
||||
|
||||
onready var credits = $AboutUI/Credits
|
||||
onready var groups : Tree = $AboutUI/Credits/Groups
|
||||
onready var developer_container = $AboutUI/Credits/Developers
|
||||
onready var contributors_container = $AboutUI/Credits/Contributors
|
||||
onready var donors_container = $AboutUI/Credits/Donors
|
||||
onready var translators_container = $AboutUI/Credits/Translators
|
||||
|
||||
onready var developers : Tree = $AboutUI/Credits/Developers/DeveloperTree
|
||||
onready var contributors : Tree = $AboutUI/Credits/Contributors/ContributorTree
|
||||
onready var donors : Tree = $AboutUI/Credits/Donors/DonorTree
|
||||
onready var translators : Tree = $AboutUI/Credits/Translators/TranslatorTree
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
var contributor_root := contributors.create_item()
|
||||
contributors.create_item(contributor_root).set_text(0, " Hugo Locurcio (Calinou)")
|
||||
contributors.create_item(contributor_root).set_text(0, " CheetoHead (greusser)")
|
||||
contributors.create_item(contributor_root).set_text(0, " Michael Alexsander (YeldhamDev)")
|
||||
contributors.create_item(contributor_root).set_text(0, " Schweini07")
|
||||
contributors.create_item(contributor_root).set_text(0, " Martin Zabinski")
|
||||
contributors.create_item(contributor_root).set_text(0, " azagaya")
|
||||
contributors.create_item(contributor_root).set_text(0, " Andreev Andrei")
|
||||
contributors.create_item(contributor_root).set_text(0, " Martin Novák (novhack)")
|
||||
contributors.create_item(contributor_root).set_text(0, " Marco Galli (Gaarco)")
|
||||
contributors.create_item(contributor_root).set_text(0, " Subhang Nanduri (SbNanduri)")
|
||||
contributors.create_item(contributor_root).set_text(0, " danielnaoexiste")
|
||||
contributors.create_item(contributor_root).set_text(0, " Noah Burck (haonkrub)")
|
||||
contributors.create_item(contributor_root).set_text(0, " Darshan Phaldesai (luiq54)")
|
||||
contributors.create_item(contributor_root).set_text(0, " Matheus Pesegoginski (MatheusPese)")
|
||||
contributors.create_item(contributor_root).set_text(0, " sapient_cogbag")
|
||||
contributors.create_item(contributor_root).set_text(0, " Kinwailo")
|
||||
contributors.create_item(contributor_root).set_text(0, " Igor Santarek (jegor377)")
|
||||
contributors.create_item(contributor_root).set_text(0, " Dávid Gábor BODOR (dragonfi)")
|
||||
|
||||
var donors_root := donors.create_item()
|
||||
donors.create_item(donors_root).set_text(0, " pcmxms")
|
||||
donors.create_item(donors_root).set_text(0, " Mike King")
|
||||
|
||||
|
||||
func _on_AboutDialog_about_to_show() -> void:
|
||||
var current_version : String = ProjectSettings.get_setting("application/config/Version")
|
||||
window_title = tr("About Pixelorama") + " " + current_version
|
||||
|
||||
var groups_root := groups.create_item()
|
||||
var developers_button := groups.create_item(groups_root)
|
||||
var contributors_button := groups.create_item(groups_root)
|
||||
var donors_button := groups.create_item(groups_root)
|
||||
var translators_button := groups.create_item(groups_root)
|
||||
|
||||
developers_button.set_text(0, " " + tr("Developers"))
|
||||
# We use metadata to avoid being affected by translations
|
||||
developers_button.set_metadata(0, "Developers")
|
||||
developers_button.select(0)
|
||||
contributors_button.set_text(0, " " + tr("Contributors"))
|
||||
contributors_button.set_metadata(0, "Contributors")
|
||||
donors_button.set_text(0, " " + tr("Donors"))
|
||||
donors_button.set_metadata(0, "Donors")
|
||||
translators_button.set_text(0, " " + tr("Translators"))
|
||||
translators_button.set_metadata(0, "Translators")
|
||||
|
||||
var dev_root := developers.create_item()
|
||||
developers.create_item(dev_root).set_text(0, " Manolis Papadeas (Overloaded) - " + tr("Lead Programmer"))
|
||||
developers.create_item(dev_root).set_text(0, " John Nikitakis (Erevos) - " + tr("UI Designer"))
|
||||
|
||||
# Translators
|
||||
var translators_root := translators.create_item()
|
||||
translators.create_item(translators_root).set_text(0, " Manolis Papadeas (Overloaded) - " + tr("Greek"))
|
||||
translators.create_item(translators_root).set_text(0, " Xenofon Konitsas (huskee) - " + tr("Greek"))
|
||||
translators.create_item(translators_root).set_text(0, " Hugo Locurcio (Calinou) - " + tr("French"))
|
||||
translators.create_item(translators_root).set_text(0, " blackjoker77777 - " + tr("French"))
|
||||
translators.create_item(translators_root).set_text(0, " Schweini07 - " + tr("German"))
|
||||
translators.create_item(translators_root).set_text(0, " Martin Zabinski (Martin1991zab) - " + tr("German"))
|
||||
translators.create_item(translators_root).set_text(0, " Dawid Niedźwiedzki (tiritto) - " + tr("Polish"))
|
||||
translators.create_item(translators_root).set_text(0, " Serhiy Dmytryshyn (dies) - " + tr("Polish"))
|
||||
translators.create_item(translators_root).set_text(0, " Michael Alexsander (YeldhamDev) - " + tr("Brazilian Portuguese"))
|
||||
translators.create_item(translators_root).set_text(0, " Cedulio Cezar (ceduliocezar) - " + tr("Brazilian Portuguese"))
|
||||
translators.create_item(translators_root).set_text(0, " Andreev Andrei - " + tr("Russian"))
|
||||
translators.create_item(translators_root).set_text(0, " ax trifonov (ax34) - " + tr("Russian"))
|
||||
translators.create_item(translators_root).set_text(0, " Artem (blinovartem) - " + tr("Russian"))
|
||||
translators.create_item(translators_root).set_text(0, " JunYouIntrovert - " + tr("Chinese Traditional"))
|
||||
translators.create_item(translators_root).set_text(0, " Chenxu Wang - " + tr("Chinese Simplified"))
|
||||
translators.create_item(translators_root).set_text(0, " Marco Galli (Gaarco) - " + tr("Italian"))
|
||||
translators.create_item(translators_root).set_text(0, " StarFang208 - " + tr("Italian"))
|
||||
translators.create_item(translators_root).set_text(0, " azagaya - " + tr("Spanish"))
|
||||
translators.create_item(translators_root).set_text(0, " Lilly And (KatieAnd) - " + tr("Spanish"))
|
||||
translators.create_item(translators_root).set_text(0, " Agnis Aldiņš (NeZvers) - " + tr("Latvian"))
|
||||
translators.create_item(translators_root).set_text(0, " Teashrock - " + tr("Esperanto"))
|
||||
|
||||
|
||||
func _on_AboutDialog_popup_hide() -> void:
|
||||
groups.clear()
|
||||
developers.clear()
|
||||
|
||||
|
||||
func _on_Groups_item_selected() -> void:
|
||||
for child in credits.get_children():
|
||||
if child != groups:
|
||||
child.visible = false
|
||||
|
||||
var selected : String = groups.get_selected().get_metadata(0)
|
||||
if "Developers" in selected:
|
||||
developer_container.visible = true
|
||||
elif "Contributors" in selected:
|
||||
contributors_container.visible = true
|
||||
elif "Donors" in selected:
|
||||
donors_container.visible = true
|
||||
elif "Translators" in selected:
|
||||
translators_container.visible = true
|
||||
|
||||
|
||||
func _on_Website_pressed() -> void:
|
||||
OS.shell_open("https://www.orama-interactive.com/pixelorama")
|
||||
|
||||
|
||||
func _on_GitHub_pressed() -> void:
|
||||
OS.shell_open("https://github.com/Orama-Interactive/Pixelorama")
|
||||
|
||||
|
||||
func _on_Donate_pressed() -> void:
|
||||
OS.shell_open("https://www.patreon.com/OramaInteractive")
|
208
src/Dialogs/AboutDialog.tscn
Normal file
208
src/Dialogs/AboutDialog.tscn
Normal file
|
@ -0,0 +1,208 @@
|
|||
[gd_scene load_steps=6 format=2]
|
||||
|
||||
[ext_resource path="res://src/Dialogs/AboutDialog.gd" type="Script" id=1]
|
||||
[ext_resource path="res://Assets/Graphics/icon_64x64.png" type="Texture" id=2]
|
||||
[ext_resource path="res://Assets/Fonts/Roboto-Italic.tres" type="DynamicFont" id=3]
|
||||
[ext_resource path="res://Assets/Graphics/orama_64x64.png" type="Texture" id=4]
|
||||
[ext_resource path="res://Assets/Fonts/Roboto-Small.tres" type="DynamicFont" id=5]
|
||||
|
||||
|
||||
[node name="AboutDialog" type="WindowDialog"]
|
||||
margin_right = 512.0
|
||||
margin_bottom = 288.0
|
||||
rect_min_size = Vector2( 512, 288 )
|
||||
window_title = "About Pixelorama"
|
||||
resizable = true
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="AboutUI" type="VBoxContainer" parent="."]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
margin_left = 12.0
|
||||
margin_top = 4.0
|
||||
margin_right = -12.0
|
||||
margin_bottom = -8.0
|
||||
alignment = 1
|
||||
|
||||
[node name="IconsButtons" type="HBoxContainer" parent="AboutUI"]
|
||||
margin_right = 488.0
|
||||
margin_bottom = 64.0
|
||||
|
||||
[node name="PixeloramaLogo" type="TextureRect" parent="AboutUI/IconsButtons"]
|
||||
margin_right = 64.0
|
||||
margin_bottom = 64.0
|
||||
texture = ExtResource( 2 )
|
||||
|
||||
[node name="SloganAndLinks" type="CenterContainer" parent="AboutUI/IconsButtons"]
|
||||
margin_left = 68.0
|
||||
margin_right = 420.0
|
||||
margin_bottom = 64.0
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="AboutUI/IconsButtons/SloganAndLinks"]
|
||||
margin_left = 64.0
|
||||
margin_top = 12.0
|
||||
margin_right = 288.0
|
||||
margin_bottom = 51.0
|
||||
|
||||
[node name="Pixelorama" type="Label" parent="AboutUI/IconsButtons/SloganAndLinks/VBoxContainer"]
|
||||
margin_right = 224.0
|
||||
margin_bottom = 15.0
|
||||
custom_fonts/font = ExtResource( 3 )
|
||||
text = "Pixelorama - Pixelate your dreams!"
|
||||
align = 1
|
||||
|
||||
[node name="LinkButtons" type="HBoxContainer" parent="AboutUI/IconsButtons/SloganAndLinks/VBoxContainer"]
|
||||
margin_top = 19.0
|
||||
margin_right = 224.0
|
||||
margin_bottom = 39.0
|
||||
|
||||
[node name="Website" type="Button" parent="AboutUI/IconsButtons/SloganAndLinks/VBoxContainer/LinkButtons"]
|
||||
margin_right = 65.0
|
||||
margin_bottom = 20.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Website"
|
||||
|
||||
[node name="GitHub" type="Button" parent="AboutUI/IconsButtons/SloganAndLinks/VBoxContainer/LinkButtons"]
|
||||
margin_left = 69.0
|
||||
margin_right = 162.0
|
||||
margin_bottom = 20.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "GitHub Repo"
|
||||
|
||||
[node name="Donate" type="Button" parent="AboutUI/IconsButtons/SloganAndLinks/VBoxContainer/LinkButtons"]
|
||||
margin_left = 166.0
|
||||
margin_right = 224.0
|
||||
margin_bottom = 20.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Donate"
|
||||
|
||||
[node name="OramaLogo" type="TextureRect" parent="AboutUI/IconsButtons"]
|
||||
margin_left = 424.0
|
||||
margin_right = 488.0
|
||||
margin_bottom = 64.0
|
||||
texture = ExtResource( 4 )
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="AboutUI"]
|
||||
margin_top = 68.0
|
||||
margin_right = 488.0
|
||||
margin_bottom = 72.0
|
||||
|
||||
[node name="Credits" type="HSplitContainer" parent="AboutUI"]
|
||||
margin_top = 76.0
|
||||
margin_right = 488.0
|
||||
margin_bottom = 233.0
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Groups" type="Tree" parent="AboutUI/Credits"]
|
||||
margin_right = 120.0
|
||||
margin_bottom = 157.0
|
||||
rect_min_size = Vector2( 120, 120 )
|
||||
custom_constants/item_margin = -2
|
||||
hide_root = true
|
||||
|
||||
[node name="Developers" type="VBoxContainer" parent="AboutUI/Credits"]
|
||||
margin_left = 132.0
|
||||
margin_right = 488.0
|
||||
margin_bottom = 157.0
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="Label" type="Label" parent="AboutUI/Credits/Developers"]
|
||||
margin_right = 356.0
|
||||
margin_bottom = 14.0
|
||||
text = "Development Team"
|
||||
|
||||
[node name="DeveloperTree" type="Tree" parent="AboutUI/Credits/Developers"]
|
||||
margin_top = 18.0
|
||||
margin_right = 356.0
|
||||
margin_bottom = 157.0
|
||||
size_flags_vertical = 3
|
||||
custom_constants/item_margin = -2
|
||||
custom_constants/button_margin = 2
|
||||
hide_root = true
|
||||
|
||||
[node name="Contributors" type="VBoxContainer" parent="AboutUI/Credits"]
|
||||
visible = false
|
||||
margin_left = 254.0
|
||||
margin_right = 496.0
|
||||
margin_bottom = 126.0
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="Label" type="Label" parent="AboutUI/Credits/Contributors"]
|
||||
margin_right = 242.0
|
||||
margin_bottom = 14.0
|
||||
text = "GitHub Contributors"
|
||||
|
||||
[node name="ContributorTree" type="Tree" parent="AboutUI/Credits/Contributors"]
|
||||
margin_top = 18.0
|
||||
margin_right = 242.0
|
||||
margin_bottom = 126.0
|
||||
size_flags_vertical = 3
|
||||
custom_constants/item_margin = -2
|
||||
hide_root = true
|
||||
|
||||
[node name="Donors" type="VBoxContainer" parent="AboutUI/Credits"]
|
||||
visible = false
|
||||
margin_left = 254.0
|
||||
margin_right = 496.0
|
||||
margin_bottom = 126.0
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="Label" type="Label" parent="AboutUI/Credits/Donors"]
|
||||
margin_right = 242.0
|
||||
margin_bottom = 14.0
|
||||
text = "Donors"
|
||||
|
||||
[node name="DonorTree" type="Tree" parent="AboutUI/Credits/Donors"]
|
||||
margin_top = 18.0
|
||||
margin_right = 242.0
|
||||
margin_bottom = 126.0
|
||||
size_flags_vertical = 3
|
||||
custom_constants/item_margin = -2
|
||||
hide_root = true
|
||||
|
||||
[node name="Translators" type="VBoxContainer" parent="AboutUI/Credits"]
|
||||
visible = false
|
||||
margin_left = 254.0
|
||||
margin_right = 496.0
|
||||
margin_bottom = 126.0
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="Label" type="Label" parent="AboutUI/Credits/Translators"]
|
||||
margin_right = 242.0
|
||||
margin_bottom = 14.0
|
||||
text = "Translators"
|
||||
|
||||
[node name="TranslatorTree" type="Tree" parent="AboutUI/Credits/Translators"]
|
||||
margin_top = 18.0
|
||||
margin_right = 242.0
|
||||
margin_bottom = 126.0
|
||||
size_flags_vertical = 3
|
||||
custom_constants/item_margin = -2
|
||||
hide_root = true
|
||||
|
||||
[node name="HSeparator2" type="HSeparator" parent="AboutUI"]
|
||||
margin_top = 237.0
|
||||
margin_right = 488.0
|
||||
margin_bottom = 241.0
|
||||
|
||||
[node name="MadeBy" type="Label" parent="AboutUI"]
|
||||
margin_top = 245.0
|
||||
margin_right = 488.0
|
||||
margin_bottom = 259.0
|
||||
text = "Developed by Orama Interactive"
|
||||
align = 1
|
||||
|
||||
[node name="Copyright" type="Label" parent="AboutUI"]
|
||||
margin_top = 263.0
|
||||
margin_right = 488.0
|
||||
margin_bottom = 276.0
|
||||
custom_fonts/font = ExtResource( 5 )
|
||||
text = "Copyright 2019-2020 Orama Interactive"
|
||||
align = 1
|
||||
[connection signal="about_to_show" from="." to="." method="_on_AboutDialog_about_to_show"]
|
||||
[connection signal="popup_hide" from="." to="." method="_on_AboutDialog_popup_hide"]
|
||||
[connection signal="pressed" from="AboutUI/IconsButtons/SloganAndLinks/VBoxContainer/LinkButtons/Website" to="." method="_on_Website_pressed"]
|
||||
[connection signal="pressed" from="AboutUI/IconsButtons/SloganAndLinks/VBoxContainer/LinkButtons/GitHub" to="." method="_on_GitHub_pressed"]
|
||||
[connection signal="pressed" from="AboutUI/IconsButtons/SloganAndLinks/VBoxContainer/LinkButtons/Donate" to="." method="_on_Donate_pressed"]
|
||||
[connection signal="item_selected" from="AboutUI/Credits/Groups" to="." method="_on_Groups_item_selected"]
|
133
src/Dialogs/CreateNewImage.gd
Normal file
133
src/Dialogs/CreateNewImage.gd
Normal file
|
@ -0,0 +1,133 @@
|
|||
extends ConfirmationDialog
|
||||
|
||||
onready var templates_options = $VBoxContainer/OptionsContainer/TemplatesOptions
|
||||
onready var ratio_box = $VBoxContainer/OptionsContainer/RatioCheckBox
|
||||
onready var width_value = $VBoxContainer/OptionsContainer/WidthValue
|
||||
onready var height_value = $VBoxContainer/OptionsContainer/HeightValue
|
||||
onready var fill_color_node = $VBoxContainer/OptionsContainer/FillColor
|
||||
|
||||
onready var size_value = Vector2()
|
||||
|
||||
# Template Id identifier
|
||||
enum Templates {
|
||||
TDefault = 0,
|
||||
T16 = 1,
|
||||
T32 = 2,
|
||||
T64 = 3,
|
||||
T128 = 4,
|
||||
GB = 5,
|
||||
GBA = 6,
|
||||
NES_NTSC = 7,
|
||||
NES_PAL = 8,
|
||||
SNES_NTSC = 9,
|
||||
SNES_PAL = 10
|
||||
}
|
||||
# Template actual value, without Default because we get it from Global
|
||||
var TResolutions = {
|
||||
Templates.T16: Vector2(16,16),
|
||||
Templates.T32: Vector2(32,32),
|
||||
Templates.T64: Vector2(64,64),
|
||||
Templates.T128: Vector2(128,128),
|
||||
|
||||
Templates.GB: Vector2(160,144),
|
||||
Templates.GBA: Vector2(240,160),
|
||||
Templates.NES_NTSC: Vector2(256,224),
|
||||
Templates.NES_PAL: Vector2(256,240),
|
||||
Templates.SNES_NTSC: Vector2(512,448),
|
||||
Templates.SNES_PAL: Vector2(512,480),
|
||||
}
|
||||
|
||||
var TStrings ={
|
||||
Templates.T16: "",
|
||||
Templates.T32: "",
|
||||
Templates.T64: "",
|
||||
Templates.T128: "",
|
||||
|
||||
Templates.GB: "GB",
|
||||
Templates.GBA: "GBA",
|
||||
Templates.NES_NTSC: "NES (NTSC)",
|
||||
Templates.NES_PAL: "NES (PAL)",
|
||||
Templates.SNES_NTSC: "SNES (NTSC)",
|
||||
Templates.SNES_PAL: "SNES (PAL)"
|
||||
}
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
ratio_box.connect("pressed", self, "_on_RatioCheckBox_toggled", [ratio_box.pressed])
|
||||
templates_options.connect("item_selected", self, "_on_TemplatesOptions_item_selected")
|
||||
|
||||
_CreateOptionList()
|
||||
|
||||
|
||||
func _CreateOptionList() -> void:
|
||||
for i in Templates.values():
|
||||
if i > 0:
|
||||
if TStrings[i] != "":
|
||||
templates_options.add_item("{width}x{height} - {name}".format({"width":TResolutions[i].x, "height":TResolutions[i].y, "name":TStrings[i]}), i)
|
||||
else:
|
||||
templates_options.add_item("{width}x{height}".format({"width":TResolutions[i].x, "height":TResolutions[i].y}), i)
|
||||
|
||||
|
||||
func _on_CreateNewImage_confirmed() -> void:
|
||||
var width : int = width_value.value
|
||||
var height : int = height_value.value
|
||||
var fill_color : Color = fill_color_node.color
|
||||
Global.clear_canvases()
|
||||
Global.layers.clear()
|
||||
# Store [Layer name (0), Layer visibility boolean (1), Layer lock boolean (2), Frame container (3),
|
||||
# will new frames be linked boolean (4), Array of linked frames (5)]
|
||||
Global.layers.append([tr("Layer") + " 0", true, false, HBoxContainer.new(), false, []])
|
||||
Global.canvas = load("res://src/Canvas.tscn").instance()
|
||||
Global.canvas.size = Vector2(width, height).floor()
|
||||
|
||||
Global.canvases.append(Global.canvas)
|
||||
Global.canvas_parent.add_child(Global.canvas)
|
||||
Global.current_layer = 0
|
||||
Global.canvases = Global.canvases # To trigger Global.canvases_changed()
|
||||
Global.current_frame = 0
|
||||
Global.layers = Global.layers # To trigger Global.layers_changed()
|
||||
Global.project_has_changed = false
|
||||
if fill_color.a > 0:
|
||||
Global.canvas.layers[0][0].fill(fill_color)
|
||||
Global.canvas.layers[0][0].lock()
|
||||
Global.canvas.update_texture(0)
|
||||
|
||||
|
||||
func _on_CreateNewImage_about_to_show() -> void:
|
||||
width_value.value = Global.default_image_width
|
||||
height_value.value = Global.default_image_height
|
||||
fill_color_node.color = Global.default_fill_color
|
||||
templates_options.selected = Templates.TDefault
|
||||
ratio_box.pressed = false
|
||||
for spin_box in [width_value, height_value]:
|
||||
if spin_box.is_connected("value_changed", self, "_on_SizeValue_value_changed"):
|
||||
spin_box.disconnect("value_changed", self, "_on_SizeValue_value_changed")
|
||||
|
||||
|
||||
var aspect_ratio: float
|
||||
|
||||
func _on_RatioCheckBox_toggled(_button_pressed: bool) -> void:
|
||||
aspect_ratio = width_value.value / height_value.value
|
||||
for spin_box in [width_value, height_value]:
|
||||
if spin_box.is_connected("value_changed", self, "_on_SizeValue_value_changed"):
|
||||
spin_box.disconnect("value_changed", self, "_on_SizeValue_value_changed")
|
||||
else:
|
||||
spin_box.connect("value_changed", self, "_on_SizeValue_value_changed")
|
||||
|
||||
|
||||
func _on_SizeValue_value_changed(value: float) -> void:
|
||||
if width_value.value == value:
|
||||
height_value.value = width_value.value / aspect_ratio
|
||||
if height_value.value == value:
|
||||
width_value.value = height_value.value * aspect_ratio
|
||||
|
||||
|
||||
func _on_TemplatesOptions_item_selected(id: int) -> void:
|
||||
if id != Templates.TDefault:
|
||||
size_value = TResolutions[id]
|
||||
else:
|
||||
width_value.value = Global.default_image_width
|
||||
height_value.value = Global.default_image_height
|
||||
|
||||
width_value.value = size_value.x
|
||||
height_value.value = size_value.y
|
123
src/Dialogs/CreateNewImage.tscn
Normal file
123
src/Dialogs/CreateNewImage.tscn
Normal file
|
@ -0,0 +1,123 @@
|
|||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://src/Dialogs/CreateNewImage.gd" type="Script" id=1]
|
||||
|
||||
|
||||
[node name="CreateNewImage" type="ConfirmationDialog"]
|
||||
margin_right = 300.0
|
||||
margin_bottom = 200.0
|
||||
rect_min_size = Vector2( 375, 200 )
|
||||
script = ExtResource( 1 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
margin_left = 8.0
|
||||
margin_top = 8.0
|
||||
margin_right = 367.0
|
||||
margin_bottom = 164.0
|
||||
size_flags_horizontal = 0
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="ImageSize" type="Label" parent="VBoxContainer"]
|
||||
margin_right = 359.0
|
||||
margin_bottom = 14.0
|
||||
text = "Image Size"
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="VBoxContainer"]
|
||||
margin_top = 18.0
|
||||
margin_right = 359.0
|
||||
margin_bottom = 22.0
|
||||
|
||||
[node name="OptionsContainer" type="GridContainer" parent="VBoxContainer"]
|
||||
margin_top = 26.0
|
||||
margin_right = 359.0
|
||||
margin_bottom = 154.0
|
||||
custom_constants/vseparation = 4
|
||||
custom_constants/hseparation = 2
|
||||
columns = 2
|
||||
|
||||
[node name="TemplatesLabel" type="Label" parent="VBoxContainer/OptionsContainer"]
|
||||
margin_top = 3.0
|
||||
margin_right = 112.0
|
||||
margin_bottom = 17.0
|
||||
text = "Templates:"
|
||||
|
||||
[node name="TemplatesOptions" type="OptionButton" parent="VBoxContainer/OptionsContainer"]
|
||||
margin_left = 114.0
|
||||
margin_right = 189.0
|
||||
margin_bottom = 20.0
|
||||
mouse_default_cursor_shape = 2
|
||||
toggle_mode = false
|
||||
text = "Default"
|
||||
items = [ "Default", null, false, 0, null ]
|
||||
selected = 0
|
||||
|
||||
[node name="RatioLabel" type="Label" parent="VBoxContainer/OptionsContainer"]
|
||||
margin_top = 29.0
|
||||
margin_right = 112.0
|
||||
margin_bottom = 43.0
|
||||
text = "Lock aspect ratio:"
|
||||
|
||||
[node name="RatioCheckBox" type="CheckBox" parent="VBoxContainer/OptionsContainer"]
|
||||
margin_left = 114.0
|
||||
margin_top = 24.0
|
||||
margin_right = 189.0
|
||||
margin_bottom = 48.0
|
||||
mouse_default_cursor_shape = 2
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="WidthLabel" type="Label" parent="VBoxContainer/OptionsContainer"]
|
||||
margin_top = 57.0
|
||||
margin_right = 112.0
|
||||
margin_bottom = 71.0
|
||||
text = "Width:"
|
||||
|
||||
[node name="WidthValue" type="SpinBox" parent="VBoxContainer/OptionsContainer"]
|
||||
margin_left = 114.0
|
||||
margin_top = 52.0
|
||||
margin_right = 189.0
|
||||
margin_bottom = 76.0
|
||||
mouse_default_cursor_shape = 2
|
||||
min_value = 1.0
|
||||
max_value = 16384.0
|
||||
value = 64.0
|
||||
suffix = "px"
|
||||
|
||||
[node name="Height" type="Label" parent="VBoxContainer/OptionsContainer"]
|
||||
margin_top = 85.0
|
||||
margin_right = 112.0
|
||||
margin_bottom = 99.0
|
||||
text = "Height:"
|
||||
|
||||
[node name="HeightValue" type="SpinBox" parent="VBoxContainer/OptionsContainer"]
|
||||
margin_left = 114.0
|
||||
margin_top = 80.0
|
||||
margin_right = 189.0
|
||||
margin_bottom = 104.0
|
||||
mouse_default_cursor_shape = 2
|
||||
min_value = 1.0
|
||||
max_value = 16384.0
|
||||
value = 64.0
|
||||
suffix = "px"
|
||||
|
||||
[node name="FillColorLabel" type="Label" parent="VBoxContainer/OptionsContainer"]
|
||||
margin_top = 111.0
|
||||
margin_right = 112.0
|
||||
margin_bottom = 125.0
|
||||
text = "Fill with color:"
|
||||
|
||||
[node name="FillColor" type="ColorPickerButton" parent="VBoxContainer/OptionsContainer"]
|
||||
margin_left = 114.0
|
||||
margin_top = 108.0
|
||||
margin_right = 189.0
|
||||
margin_bottom = 128.0
|
||||
rect_min_size = Vector2( 64, 20 )
|
||||
color = Color( 0, 0, 0, 0 )
|
||||
[connection signal="about_to_show" from="." to="." method="_on_CreateNewImage_about_to_show"]
|
||||
[connection signal="confirmed" from="." to="." method="_on_CreateNewImage_confirmed"]
|
629
src/Dialogs/ExportDialog.gd
Normal file
629
src/Dialogs/ExportDialog.gd
Normal file
|
@ -0,0 +1,629 @@
|
|||
extends AcceptDialog
|
||||
|
||||
enum ExportTab { FRAME = 0, SPRITESHEET = 1, ANIMATION = 2 }
|
||||
var current_tab : int = ExportTab.FRAME
|
||||
|
||||
# All canvases and their layers processed/blended into images
|
||||
var processed_images = [] # Image[]
|
||||
|
||||
# Frame options
|
||||
var frame_number := 0
|
||||
|
||||
# Spritesheet options
|
||||
enum Orientation { ROWS = 0, COLUMNS = 1 }
|
||||
var orientation : int = Orientation.ROWS
|
||||
# How many rows/columns before new line is added
|
||||
var lines_count := 1
|
||||
|
||||
# Animation options
|
||||
enum AnimationType { MULTIPLE_FILES = 0, ANIMATED = 1 }
|
||||
var animation_type : int = AnimationType.MULTIPLE_FILES
|
||||
var background_color : Color = Color.white
|
||||
enum AnimationDirection { FORWARD = 0, BACKWARDS = 1, PING_PONG = 2 }
|
||||
var direction : int = AnimationDirection.FORWARD
|
||||
|
||||
# Options
|
||||
var resize := 100
|
||||
var interpolation := 0 # Image.Interpolation
|
||||
var new_dir_for_each_frame_tag : bool = true # you don't need to store this after export
|
||||
|
||||
# Export directory path and export file name
|
||||
var directory_path := ""
|
||||
var file_name := ""
|
||||
var file_format : int = FileFormat.PNG
|
||||
enum FileFormat { PNG = 0, GIF = 1}
|
||||
|
||||
var file_exists_alert = "File %s already exists. Overwrite?"
|
||||
|
||||
# Store all settings after export, enables a quick re-export with same settings
|
||||
var was_exported : bool = false
|
||||
var exported_tab : int
|
||||
var exported_frame_number : int
|
||||
var exported_orientation : int
|
||||
var exported_lines_count : int
|
||||
var exported_animation_type : int
|
||||
var exported_background_color : Color
|
||||
var exported_direction : int
|
||||
var exported_resize : int
|
||||
var exported_interpolation : int
|
||||
var exported_directory_path : String
|
||||
var exported_file_name : String
|
||||
var exported_file_format : int
|
||||
|
||||
# Export coroutine signal
|
||||
signal resume_export_function()
|
||||
var stop_export = false
|
||||
|
||||
var animated_preview_current_frame := 0
|
||||
var animated_preview_frames = []
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
$VBoxContainer/Tabs.add_tab("Frame")
|
||||
$VBoxContainer/Tabs.add_tab("Spritesheet")
|
||||
$VBoxContainer/Tabs.add_tab("Animation")
|
||||
if OS.get_name() == "Windows":
|
||||
add_button("Cancel", true, "cancel")
|
||||
$Popups/FileExistsAlert.add_button("Cancel Export", true, "cancel")
|
||||
else:
|
||||
add_button("Cancel", false, "cancel")
|
||||
$Popups/FileExistsAlert.add_button("Cancel Export", false, "cancel")
|
||||
|
||||
# Disable GIF export for unsupported platforms
|
||||
if not $GifExporter.is_platform_supported():
|
||||
$VBoxContainer/AnimationOptions/AnimationType.selected = AnimationType.MULTIPLE_FILES
|
||||
$VBoxContainer/AnimationOptions/AnimationType.disabled = true
|
||||
|
||||
|
||||
func show_tab() -> void:
|
||||
$VBoxContainer/FrameOptions.hide()
|
||||
$VBoxContainer/SpritesheetOptions.hide()
|
||||
$VBoxContainer/AnimationOptions.hide()
|
||||
|
||||
match current_tab:
|
||||
ExportTab.FRAME:
|
||||
file_format = FileFormat.PNG
|
||||
$VBoxContainer/File/FileFormat.selected = FileFormat.PNG
|
||||
$FrameTimer.stop()
|
||||
if not was_exported:
|
||||
frame_number = Global.current_frame + 1
|
||||
$VBoxContainer/FrameOptions/FrameNumber/FrameNumber.max_value = Global.canvases.size() + 1
|
||||
$VBoxContainer/FrameOptions/FrameNumber/FrameNumber.value = frame_number
|
||||
process_frame()
|
||||
$VBoxContainer/FrameOptions.show()
|
||||
ExportTab.SPRITESHEET:
|
||||
file_format = FileFormat.PNG
|
||||
$VBoxContainer/File/FileFormat.selected = FileFormat.PNG
|
||||
$FrameTimer.stop()
|
||||
if not was_exported:
|
||||
orientation = Orientation.ROWS
|
||||
lines_count = int(ceil(sqrt(Global.canvases.size())))
|
||||
$VBoxContainer/SpritesheetOptions/Orientation/Orientation.selected = orientation
|
||||
$VBoxContainer/SpritesheetOptions/Orientation/LinesCount.max_value = Global.canvases.size()
|
||||
$VBoxContainer/SpritesheetOptions/Orientation/LinesCount.value = lines_count
|
||||
$VBoxContainer/SpritesheetOptions/Orientation/LinesCountLabel.text = "Columns:"
|
||||
process_spritesheet()
|
||||
$VBoxContainer/SpritesheetOptions.show()
|
||||
ExportTab.ANIMATION:
|
||||
set_file_format_selector()
|
||||
process_animation()
|
||||
$VBoxContainer/AnimationOptions/AnimationType.selected = animation_type
|
||||
$VBoxContainer/AnimationOptions/AnimatedOptions/BackgroundColor.color = background_color
|
||||
$VBoxContainer/AnimationOptions/AnimatedOptions/Direction.selected = direction
|
||||
$VBoxContainer/AnimationOptions.show()
|
||||
set_preview()
|
||||
$VBoxContainer/Tabs.current_tab = current_tab
|
||||
|
||||
|
||||
func external_export() -> void:
|
||||
restore_previous_export_settings()
|
||||
match current_tab:
|
||||
ExportTab.FRAME:
|
||||
process_frame()
|
||||
ExportTab.SPRITESHEET:
|
||||
process_spritesheet()
|
||||
ExportTab.ANIMATION:
|
||||
process_animation()
|
||||
export_processed_images(true)
|
||||
|
||||
|
||||
func process_frame() -> void:
|
||||
var canvas = Global.canvases[frame_number - 1]
|
||||
var image := Image.new()
|
||||
image.create(canvas.size.x, canvas.size.y, false, Image.FORMAT_RGBA8)
|
||||
blend_layers(image, canvas)
|
||||
processed_images.clear()
|
||||
processed_images.append(image)
|
||||
|
||||
|
||||
func process_spritesheet() -> void:
|
||||
# If rows mode selected calculate columns count and vice versa
|
||||
var spritesheet_columns = lines_count if orientation == Orientation.ROWS else frames_divided_by_spritesheet_lines()
|
||||
var spritesheet_rows = lines_count if orientation == Orientation.COLUMNS else frames_divided_by_spritesheet_lines()
|
||||
|
||||
var width = Global.canvas.size.x * spritesheet_columns
|
||||
var height = Global.canvas.size.y * spritesheet_rows
|
||||
|
||||
var whole_image := Image.new()
|
||||
whole_image.create(width, height, false, Image.FORMAT_RGBA8)
|
||||
whole_image.lock()
|
||||
var origin := Vector2.ZERO
|
||||
var hh := 0
|
||||
var vv := 0
|
||||
for canvas in Global.canvases:
|
||||
if orientation == Orientation.ROWS:
|
||||
if vv < spritesheet_columns:
|
||||
origin.x = canvas.size.x * vv
|
||||
vv += 1
|
||||
else:
|
||||
hh += 1
|
||||
origin.x = 0
|
||||
vv = 1
|
||||
origin.y = canvas.size.y * hh
|
||||
else:
|
||||
if hh < spritesheet_rows:
|
||||
origin.y = canvas.size.y * hh
|
||||
hh += 1
|
||||
else:
|
||||
vv += 1
|
||||
origin.y = 0
|
||||
hh = 1
|
||||
origin.x = canvas.size.x * vv
|
||||
blend_layers(whole_image, canvas, origin)
|
||||
|
||||
processed_images.clear()
|
||||
processed_images.append(whole_image)
|
||||
|
||||
|
||||
func process_animation() -> void:
|
||||
processed_images.clear()
|
||||
for canvas in Global.canvases:
|
||||
var image := Image.new()
|
||||
image.create(canvas.size.x, canvas.size.y, false, Image.FORMAT_RGBA8)
|
||||
blend_layers(image, canvas)
|
||||
processed_images.append(image)
|
||||
|
||||
|
||||
func set_preview() -> void:
|
||||
remove_previews()
|
||||
if processed_images.size() == 1 and current_tab != ExportTab.ANIMATION:
|
||||
$VBoxContainer/PreviewScroll/Previews.columns = 1
|
||||
add_image_preview(processed_images[0])
|
||||
else:
|
||||
match animation_type:
|
||||
AnimationType.MULTIPLE_FILES:
|
||||
$VBoxContainer/PreviewScroll/Previews.columns = ceil(sqrt(processed_images.size()))
|
||||
for i in range(processed_images.size()):
|
||||
add_image_preview(processed_images[i], i + 1)
|
||||
AnimationType.ANIMATED:
|
||||
$VBoxContainer/PreviewScroll/Previews.columns = 1
|
||||
add_animated_preview()
|
||||
|
||||
|
||||
func add_image_preview(image: Image, canvas_number: int = -1) -> void:
|
||||
var container = create_preview_container()
|
||||
var preview = create_preview_rect()
|
||||
preview.texture = ImageTexture.new()
|
||||
preview.texture.create_from_image(image, 0)
|
||||
container.add_child(preview)
|
||||
|
||||
if canvas_number != -1:
|
||||
var label = Label.new()
|
||||
label.align = Label.ALIGN_CENTER
|
||||
label.text = String(canvas_number)
|
||||
container.add_child(label)
|
||||
|
||||
$VBoxContainer/PreviewScroll/Previews.add_child(container)
|
||||
|
||||
|
||||
func add_animated_preview() -> void:
|
||||
animated_preview_current_frame = processed_images.size() - 1 if direction == AnimationDirection.BACKWARDS else 0
|
||||
animated_preview_frames = []
|
||||
|
||||
for processed_image in processed_images:
|
||||
var texture = ImageTexture.new()
|
||||
texture.create_from_image(processed_image, 0)
|
||||
animated_preview_frames.push_back(texture)
|
||||
|
||||
var container = create_preview_container()
|
||||
container.name = "PreviewContainer"
|
||||
var preview = create_preview_rect()
|
||||
preview.name = "Preview"
|
||||
preview.texture = animated_preview_frames[animated_preview_current_frame]
|
||||
container.add_child(preview)
|
||||
|
||||
$VBoxContainer/PreviewScroll/Previews.add_child(container)
|
||||
$FrameTimer.start()
|
||||
|
||||
|
||||
func create_preview_container() -> VBoxContainer:
|
||||
var container = VBoxContainer.new()
|
||||
container.size_flags_horizontal = SIZE_EXPAND_FILL
|
||||
container.size_flags_vertical = SIZE_EXPAND_FILL
|
||||
container.rect_min_size = Vector2(0, 128)
|
||||
return container
|
||||
|
||||
|
||||
func create_preview_rect() -> TextureRect:
|
||||
var preview = TextureRect.new()
|
||||
preview.expand = true
|
||||
preview.size_flags_horizontal = SIZE_EXPAND_FILL
|
||||
preview.size_flags_vertical = SIZE_EXPAND_FILL
|
||||
preview.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||
return preview
|
||||
|
||||
|
||||
func remove_previews() -> void:
|
||||
for child in $VBoxContainer/PreviewScroll/Previews.get_children():
|
||||
child.free()
|
||||
|
||||
|
||||
func get_proccessed_image_animation_tag_and_start_id(processed_image_id : int) -> Array:
|
||||
var result_animation_tag_and_start_id = null
|
||||
for animation_tag in Global.animation_tags:
|
||||
# Check if processed image is in frame tag and assign frame tag and start id if yes
|
||||
# Then stop
|
||||
if (processed_image_id + 1) >= animation_tag[2] and (processed_image_id + 1) <= animation_tag[3]:
|
||||
result_animation_tag_and_start_id = [animation_tag[0], animation_tag[2]]
|
||||
break
|
||||
return result_animation_tag_and_start_id
|
||||
|
||||
|
||||
func export_processed_images(ignore_overwrites : bool) -> void:
|
||||
# Stop export if directory path or file name are not valid
|
||||
var dir = Directory.new()
|
||||
if not dir.dir_exists(directory_path) or not file_name.is_valid_filename():
|
||||
$Popups/PathValidationAlert.popup_centered()
|
||||
return
|
||||
|
||||
# Check export paths
|
||||
var export_paths = []
|
||||
for i in range(processed_images.size()):
|
||||
stop_export = false
|
||||
var multiple_files := true if (current_tab == ExportTab.ANIMATION && animation_type == AnimationType.MULTIPLE_FILES) else false
|
||||
var export_path = create_export_path(multiple_files, i + 1)
|
||||
# If user want to create new directory for each animation tag then check if directories exist and create them if not
|
||||
if multiple_files and new_dir_for_each_frame_tag:
|
||||
var frame_tag_directory := Directory.new()
|
||||
if not frame_tag_directory.dir_exists(export_path.get_base_dir()):
|
||||
frame_tag_directory.open(directory_path)
|
||||
frame_tag_directory.make_dir(export_path.get_base_dir().get_file())
|
||||
# Check if the file already exists
|
||||
var fileCheck = File.new()
|
||||
if fileCheck.file_exists(export_path):
|
||||
# Ask user if he want's to overwrite the file
|
||||
if not was_exported or (was_exported and not ignore_overwrites):
|
||||
# Overwrite existing file?
|
||||
$Popups/FileExistsAlert.dialog_text = file_exists_alert % export_path
|
||||
$Popups/FileExistsAlert.popup_centered()
|
||||
# Stops the function until the user decides if he want's to overwrite
|
||||
yield(self, "resume_export_function")
|
||||
if stop_export:
|
||||
# User decided to stop export
|
||||
return
|
||||
export_paths.append(export_path)
|
||||
# Only get one export path if single file animated image is exported
|
||||
if current_tab == ExportTab.ANIMATION && animation_type == AnimationType.ANIMATED:
|
||||
break
|
||||
|
||||
# Scale images that are to export
|
||||
scale_processed_images()
|
||||
|
||||
if current_tab == ExportTab.ANIMATION && animation_type == AnimationType.ANIMATED:
|
||||
var frame_delay_in_ms = Global.animation_timer.wait_time * 100
|
||||
|
||||
$GifExporter.begin_export(export_paths[0], processed_images[0].get_width(), processed_images[0].get_height(), frame_delay_in_ms, 0)
|
||||
match direction:
|
||||
AnimationDirection.FORWARD:
|
||||
for i in range(processed_images.size()):
|
||||
$GifExporter.write_frame(processed_images[i], background_color, frame_delay_in_ms)
|
||||
AnimationDirection.BACKWARDS:
|
||||
for i in range(processed_images.size() - 1, -1, -1):
|
||||
$GifExporter.write_frame(processed_images[i], background_color, frame_delay_in_ms)
|
||||
AnimationDirection.PING_PONG:
|
||||
for i in range(0, processed_images.size()):
|
||||
$GifExporter.write_frame(processed_images[i], background_color, frame_delay_in_ms)
|
||||
for i in range(processed_images.size() - 2, 0, -1):
|
||||
$GifExporter.write_frame(processed_images[i], background_color, frame_delay_in_ms)
|
||||
$GifExporter.end_export()
|
||||
else:
|
||||
for i in range(processed_images.size()):
|
||||
var err = processed_images[i].save_png(export_paths[i])
|
||||
if err != OK:
|
||||
OS.alert("Can't save file")
|
||||
|
||||
# Store settings for quick export and when the dialog is opened again
|
||||
was_exported = true
|
||||
store_export_settings()
|
||||
Global.file_menu.get_popup().set_item_text(6, tr("Export") + " %s" % (file_name + file_format_string(file_format)))
|
||||
Global.notification_label("File(s) exported")
|
||||
hide()
|
||||
|
||||
|
||||
# Blends canvas layers into passed image starting from the origin position
|
||||
func blend_layers(image: Image, canvas: Canvas, origin: Vector2 = Vector2(0, 0)) -> void:
|
||||
image.lock()
|
||||
var layer_i := 0
|
||||
for layer in canvas.layers:
|
||||
if Global.layers[layer_i][1]:
|
||||
var layer_image := Image.new()
|
||||
layer_image.copy_from(layer[0])
|
||||
layer_image.lock()
|
||||
if layer[2] < 1: # If we have layer transparency
|
||||
for xx in layer_image.get_size().x:
|
||||
for yy in layer_image.get_size().y:
|
||||
var pixel_color := layer_image.get_pixel(xx, yy)
|
||||
var alpha : float = pixel_color.a * layer[2]
|
||||
layer_image.set_pixel(xx, yy, Color(pixel_color.r, pixel_color.g, pixel_color.b, alpha))
|
||||
canvas.blend_rect(image, layer_image, Rect2(canvas.position, canvas.size), origin)
|
||||
layer_i += 1
|
||||
image.unlock()
|
||||
|
||||
|
||||
func scale_processed_images() -> void:
|
||||
for processed_image in processed_images:
|
||||
if resize != 100:
|
||||
processed_image.unlock()
|
||||
processed_image.resize(processed_image.get_size().x * resize / 100, processed_image.get_size().y * resize / 100, interpolation)
|
||||
|
||||
|
||||
func create_export_path(multifile: bool, frame: int = 0) -> String:
|
||||
var path = file_name
|
||||
# Only append frame number when there are multiple files exported
|
||||
if multifile:
|
||||
var frame_tag_and_start_id = get_proccessed_image_animation_tag_and_start_id(frame - 1)
|
||||
# Check if exported frame is in frame tag
|
||||
if frame_tag_and_start_id != null:
|
||||
var frame_tag = frame_tag_and_start_id[0]
|
||||
var start_id = frame_tag_and_start_id[1]
|
||||
# Remove unallowed characters in frame tag directory
|
||||
var regex := RegEx.new()
|
||||
regex.compile("[^a-zA-Z0-9_]+")
|
||||
var frame_tag_dir = regex.sub(frame_tag, "", true)
|
||||
if new_dir_for_each_frame_tag:
|
||||
# Add frame tag if frame has one
|
||||
# (frame - start_id + 1) Makes frames id to start from 1 in each frame tag directory
|
||||
path += "_" + frame_tag_dir + "_" + String(frame - start_id + 1)
|
||||
return directory_path.plus_file(frame_tag_dir).plus_file(path + file_format_string(file_format))
|
||||
else:
|
||||
# Add frame tag if frame has one
|
||||
# (frame - start_id + 1) Makes frames id to start from 1 in each frame tag
|
||||
path += "_" + frame_tag_dir + "_" + String(frame - start_id + 1)
|
||||
else:
|
||||
path += "_" + String(frame)
|
||||
|
||||
return directory_path.plus_file(path + file_format_string(file_format))
|
||||
|
||||
|
||||
func frames_divided_by_spritesheet_lines() -> int:
|
||||
return int(ceil(Global.canvases.size() / float(lines_count)))
|
||||
|
||||
|
||||
func file_format_string(format_enum : int) -> String:
|
||||
match format_enum:
|
||||
0: # PNG
|
||||
return '.png'
|
||||
1: # GIF
|
||||
return '.gif'
|
||||
_:
|
||||
return ''
|
||||
|
||||
|
||||
func set_file_format_selector() -> void:
|
||||
$VBoxContainer/AnimationOptions/MultipleAnimationsDirectories.visible = false
|
||||
match animation_type:
|
||||
AnimationType.MULTIPLE_FILES:
|
||||
file_format = FileFormat.PNG
|
||||
$VBoxContainer/File/FileFormat.selected = FileFormat.PNG
|
||||
$FrameTimer.stop()
|
||||
$VBoxContainer/AnimationOptions/AnimatedOptions.hide()
|
||||
$VBoxContainer/AnimationOptions/MultipleAnimationsDirectories.pressed = new_dir_for_each_frame_tag
|
||||
$VBoxContainer/AnimationOptions/MultipleAnimationsDirectories.visible = true
|
||||
AnimationType.ANIMATED:
|
||||
file_format = FileFormat.GIF
|
||||
$VBoxContainer/File/FileFormat.selected = FileFormat.GIF
|
||||
$FrameTimer.wait_time = Global.animation_timer.wait_time
|
||||
$VBoxContainer/AnimationOptions/AnimatedOptions.show()
|
||||
|
||||
|
||||
func store_export_settings() -> void:
|
||||
exported_tab = current_tab
|
||||
exported_frame_number = frame_number
|
||||
exported_orientation = orientation
|
||||
exported_lines_count = lines_count
|
||||
exported_animation_type = animation_type
|
||||
exported_background_color = background_color
|
||||
exported_direction = direction
|
||||
exported_resize = resize
|
||||
exported_interpolation = interpolation
|
||||
exported_directory_path = directory_path
|
||||
exported_file_name = file_name
|
||||
exported_file_format = file_format
|
||||
|
||||
|
||||
# Fill the dialog with previous export settings
|
||||
func restore_previous_export_settings() -> void:
|
||||
current_tab = exported_tab
|
||||
frame_number = exported_frame_number if exported_frame_number <= Global.canvases.size() else Global.canvases.size()
|
||||
orientation = exported_orientation
|
||||
lines_count = exported_lines_count
|
||||
animation_type = exported_animation_type
|
||||
background_color = exported_background_color
|
||||
direction = exported_direction
|
||||
resize = exported_resize
|
||||
interpolation = exported_interpolation
|
||||
directory_path = exported_directory_path
|
||||
file_name = exported_file_name
|
||||
file_format = exported_file_format
|
||||
|
||||
|
||||
func _on_ExportDialog_about_to_show() -> void:
|
||||
# If export already occured - fill the dialog with previous export settings
|
||||
if was_exported:
|
||||
restore_previous_export_settings()
|
||||
|
||||
if directory_path.empty():
|
||||
directory_path = OS.get_system_dir(OS.SYSTEM_DIR_DESKTOP)
|
||||
|
||||
# If export already occured - sets gui to show previous settings
|
||||
$VBoxContainer/Options/Resize.value = resize
|
||||
$VBoxContainer/Options/Interpolation.selected = interpolation
|
||||
$VBoxContainer/Path/PathLineEdit.text = directory_path
|
||||
$VBoxContainer/File/FileLineEdit.text = file_name
|
||||
$VBoxContainer/File/FileFormat.selected = file_format
|
||||
show_tab()
|
||||
|
||||
for child in $Popups.get_children(): # Set the theme for the popups
|
||||
child.theme = Global.control.theme
|
||||
|
||||
file_exists_alert = tr("File %s already exists. Overwrite?") # Update translation
|
||||
#$VBoxContainer/Tabs.set_tab_title(0, "Frame")
|
||||
|
||||
|
||||
func _on_Tabs_tab_clicked(tab : int) -> void:
|
||||
current_tab = tab
|
||||
show_tab()
|
||||
|
||||
|
||||
func _on_Frame_value_changed(value: float) -> void:
|
||||
frame_number = value
|
||||
process_frame()
|
||||
set_preview()
|
||||
|
||||
|
||||
func _on_Orientation_item_selected(id : int) -> void:
|
||||
orientation = id
|
||||
if orientation == Orientation.ROWS:
|
||||
$VBoxContainer/SpritesheetOptions/Orientation/LinesCountLabel.text = "Columns:"
|
||||
else:
|
||||
$VBoxContainer/SpritesheetOptions/Orientation/LinesCountLabel.text = "Rows:"
|
||||
$VBoxContainer/SpritesheetOptions/Orientation/LinesCount.value = frames_divided_by_spritesheet_lines()
|
||||
process_spritesheet()
|
||||
set_preview()
|
||||
|
||||
|
||||
func _on_LinesCount_value_changed(value : float) -> void:
|
||||
lines_count = value
|
||||
process_spritesheet()
|
||||
set_preview()
|
||||
|
||||
|
||||
func _on_AnimationType_item_selected(id : int) -> void:
|
||||
animation_type = id
|
||||
set_file_format_selector()
|
||||
set_preview()
|
||||
|
||||
|
||||
func _on_BackgroundColor_color_changed(color : Color) -> void:
|
||||
background_color = color
|
||||
|
||||
|
||||
func _on_Direction_item_selected(id : int) -> void:
|
||||
direction = id
|
||||
match id:
|
||||
AnimationDirection.FORWARD:
|
||||
animated_preview_current_frame = 0
|
||||
AnimationDirection.BACKWARDS:
|
||||
animated_preview_current_frame = processed_images.size() - 1
|
||||
AnimationDirection.PING_PONG:
|
||||
animated_preview_current_frame = 0
|
||||
pingpong_direction = AnimationDirection.FORWARD
|
||||
|
||||
|
||||
func _on_Resize_value_changed(value : float) -> void:
|
||||
resize = value
|
||||
|
||||
|
||||
func _on_Interpolation_item_selected(id: int) -> void:
|
||||
interpolation = id
|
||||
|
||||
|
||||
func _on_ExportDialog_confirmed() -> void:
|
||||
export_processed_images(false)
|
||||
|
||||
|
||||
func _on_ExportDialog_custom_action(action : String) -> void:
|
||||
if action == "cancel":
|
||||
hide()
|
||||
|
||||
|
||||
func _on_PathButton_pressed() -> void:
|
||||
$Popups/PathDialog.popup_centered()
|
||||
|
||||
|
||||
func _on_PathLineEdit_text_changed(new_text : String) -> void:
|
||||
directory_path = new_text
|
||||
|
||||
|
||||
func _on_FileLineEdit_text_changed(new_text : String) -> void:
|
||||
file_name = new_text
|
||||
|
||||
|
||||
func _on_FileDialog_dir_selected(dir : String) -> void:
|
||||
$VBoxContainer/Path/PathLineEdit.text = dir
|
||||
directory_path = dir
|
||||
|
||||
|
||||
func _on_FileFormat_item_selected(id : int) -> void:
|
||||
file_format = id
|
||||
|
||||
|
||||
func _on_FileExistsAlert_confirmed() -> void:
|
||||
# Overwrite existing file
|
||||
$Popups/FileExistsAlert.dialog_text = file_exists_alert
|
||||
stop_export = false
|
||||
emit_signal("resume_export_function")
|
||||
|
||||
|
||||
func _on_FileExistsAlert_custom_action(action : String) -> void:
|
||||
if action == "cancel":
|
||||
# Cancel export
|
||||
$Popups/FileExistsAlert.dialog_text = file_exists_alert
|
||||
stop_export = true
|
||||
emit_signal("resume_export_function")
|
||||
$Popups/FileExistsAlert.hide()
|
||||
|
||||
|
||||
var pingpong_direction = AnimationDirection.FORWARD
|
||||
func _on_FrameTimer_timeout() -> void:
|
||||
$VBoxContainer/PreviewScroll/Previews/PreviewContainer/Preview.texture = animated_preview_frames[animated_preview_current_frame]
|
||||
|
||||
match direction:
|
||||
AnimationDirection.FORWARD:
|
||||
if animated_preview_current_frame == animated_preview_frames.size() - 1:
|
||||
animated_preview_current_frame = 0
|
||||
else:
|
||||
animated_preview_current_frame += 1
|
||||
|
||||
AnimationDirection.BACKWARDS:
|
||||
if animated_preview_current_frame == 0:
|
||||
animated_preview_current_frame = processed_images.size() - 1
|
||||
else:
|
||||
animated_preview_current_frame -= 1
|
||||
|
||||
AnimationDirection.PING_PONG:
|
||||
match pingpong_direction:
|
||||
AnimationDirection.FORWARD:
|
||||
if animated_preview_current_frame == animated_preview_frames.size() - 1:
|
||||
pingpong_direction = AnimationDirection.BACKWARDS
|
||||
animated_preview_current_frame -= 1
|
||||
if animated_preview_current_frame <= 0:
|
||||
animated_preview_current_frame = 0
|
||||
else:
|
||||
animated_preview_current_frame += 1
|
||||
AnimationDirection.BACKWARDS:
|
||||
if animated_preview_current_frame == 0:
|
||||
animated_preview_current_frame += 1
|
||||
if animated_preview_current_frame >= animated_preview_frames.size() - 1:
|
||||
animated_preview_current_frame = 0
|
||||
pingpong_direction = AnimationDirection.FORWARD
|
||||
else:
|
||||
animated_preview_current_frame -= 1
|
||||
|
||||
|
||||
func _on_ExportDialog_popup_hide() -> void:
|
||||
$FrameTimer.stop()
|
||||
|
||||
|
||||
func _on_MultipleAnimationsDirectories_toggled(button_pressed : bool) -> void:
|
||||
new_dir_for_each_frame_tag = button_pressed
|
389
src/Dialogs/ExportDialog.tscn
Normal file
389
src/Dialogs/ExportDialog.tscn
Normal file
|
@ -0,0 +1,389 @@
|
|||
[gd_scene load_steps=3 format=2]
|
||||
|
||||
[ext_resource path="res://src/Dialogs/ExportDialog.gd" type="Script" id=1]
|
||||
[ext_resource path="res://addons/godot-gifexporter/src/GifExporter.gd" type="Script" id=2]
|
||||
|
||||
|
||||
[node name="ExportDialog" type="AcceptDialog"]
|
||||
margin_right = 532.0
|
||||
margin_bottom = 530.0
|
||||
rect_min_size = Vector2( 456, 530 )
|
||||
window_title = "Export..."
|
||||
resizable = true
|
||||
dialog_hide_on_ok = false
|
||||
script = ExtResource( 1 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
margin_left = 8.0
|
||||
margin_top = 8.0
|
||||
margin_right = 524.0
|
||||
margin_bottom = 494.0
|
||||
rect_min_size = Vector2( 330, 0 )
|
||||
size_flags_vertical = 3
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Tabs" type="Tabs" parent="VBoxContainer"]
|
||||
margin_right = 516.0
|
||||
margin_bottom = 24.0
|
||||
size_flags_vertical = 0
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="VBoxContainer"]
|
||||
margin_top = 28.0
|
||||
margin_right = 516.0
|
||||
margin_bottom = 32.0
|
||||
|
||||
[node name="PreviewLabel" type="Label" parent="VBoxContainer"]
|
||||
margin_top = 36.0
|
||||
margin_right = 516.0
|
||||
margin_bottom = 50.0
|
||||
text = "Preview:"
|
||||
|
||||
[node name="PreviewScroll" type="ScrollContainer" parent="VBoxContainer"]
|
||||
margin_top = 54.0
|
||||
margin_right = 516.0
|
||||
margin_bottom = 274.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Previews" type="GridContainer" parent="VBoxContainer/PreviewScroll"]
|
||||
margin_right = 516.0
|
||||
margin_bottom = 220.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
columns = 3
|
||||
|
||||
[node name="FrameOptions" type="VBoxContainer" parent="VBoxContainer"]
|
||||
margin_top = 278.0
|
||||
margin_right = 516.0
|
||||
margin_bottom = 302.0
|
||||
|
||||
[node name="FrameNumber" type="HBoxContainer" parent="VBoxContainer/FrameOptions"]
|
||||
margin_right = 516.0
|
||||
margin_bottom = 24.0
|
||||
|
||||
[node name="FrameNumberLabel" type="Label" parent="VBoxContainer/FrameOptions/FrameNumber"]
|
||||
margin_top = 5.0
|
||||
margin_right = 44.0
|
||||
margin_bottom = 19.0
|
||||
text = "Frame:"
|
||||
|
||||
[node name="FrameNumber" type="SpinBox" parent="VBoxContainer/FrameOptions/FrameNumber"]
|
||||
margin_left = 48.0
|
||||
margin_right = 516.0
|
||||
margin_bottom = 24.0
|
||||
rect_min_size = Vector2( 100, 0 )
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
min_value = 1.0
|
||||
page = 1.0
|
||||
value = 1.0
|
||||
rounded = true
|
||||
align = 2
|
||||
|
||||
[node name="SpritesheetOptions" type="VBoxContainer" parent="VBoxContainer"]
|
||||
margin_top = 306.0
|
||||
margin_right = 516.0
|
||||
margin_bottom = 330.0
|
||||
|
||||
[node name="Orientation" type="HBoxContainer" parent="VBoxContainer/SpritesheetOptions"]
|
||||
margin_right = 516.0
|
||||
margin_bottom = 24.0
|
||||
alignment = 1
|
||||
|
||||
[node name="OrientationLabel" type="Label" parent="VBoxContainer/SpritesheetOptions/Orientation"]
|
||||
margin_top = 5.0
|
||||
margin_right = 77.0
|
||||
margin_bottom = 19.0
|
||||
text = "Orientation:"
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Orientation" type="OptionButton" parent="VBoxContainer/SpritesheetOptions/Orientation"]
|
||||
margin_left = 81.0
|
||||
margin_right = 264.0
|
||||
margin_bottom = 24.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Rows"
|
||||
items = [ "Rows", null, false, 0, null, "Columns", null, false, 1, null ]
|
||||
selected = 0
|
||||
|
||||
[node name="LinesCountLabel" type="Label" parent="VBoxContainer/SpritesheetOptions/Orientation"]
|
||||
margin_left = 268.0
|
||||
margin_top = 5.0
|
||||
margin_right = 328.0
|
||||
margin_bottom = 19.0
|
||||
text = "Columns:"
|
||||
|
||||
[node name="LinesCount" type="SpinBox" parent="VBoxContainer/SpritesheetOptions/Orientation"]
|
||||
margin_left = 332.0
|
||||
margin_right = 516.0
|
||||
margin_bottom = 24.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
min_value = 1.0
|
||||
max_value = 1000.0
|
||||
value = 1.0
|
||||
align = 2
|
||||
|
||||
[node name="AnimationOptions" type="VBoxContainer" parent="VBoxContainer"]
|
||||
margin_top = 334.0
|
||||
margin_right = 516.0
|
||||
margin_bottom = 386.0
|
||||
|
||||
[node name="AnimationType" type="OptionButton" parent="VBoxContainer/AnimationOptions"]
|
||||
margin_right = 516.0
|
||||
margin_bottom = 24.0
|
||||
rect_min_size = Vector2( 0, 24 )
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "All frames as multiple files"
|
||||
items = [ "All frames as multiple files", null, false, 0, null, "All frames as a single file animation", null, false, 1, null ]
|
||||
selected = 0
|
||||
|
||||
[node name="MultipleAnimationsDirectories" type="CheckBox" parent="VBoxContainer/AnimationOptions"]
|
||||
visible = false
|
||||
margin_top = 28.0
|
||||
margin_right = 516.0
|
||||
margin_bottom = 52.0
|
||||
hint_tooltip = "Creates multiple files but every file is stored in different directory that corresponds to its frame tag"
|
||||
text = "Create new directory for each frame tag"
|
||||
|
||||
[node name="AnimatedOptions" type="HBoxContainer" parent="VBoxContainer/AnimationOptions"]
|
||||
margin_top = 28.0
|
||||
margin_right = 516.0
|
||||
margin_bottom = 52.0
|
||||
rect_min_size = Vector2( 0, 24 )
|
||||
|
||||
[node name="BackgroundColorLabel" type="Label" parent="VBoxContainer/AnimationOptions/AnimatedOptions"]
|
||||
margin_top = 5.0
|
||||
margin_right = 78.0
|
||||
margin_bottom = 19.0
|
||||
text = "Background:"
|
||||
valign = 1
|
||||
|
||||
[node name="BackgroundColor" type="ColorPickerButton" parent="VBoxContainer/AnimationOptions/AnimatedOptions"]
|
||||
margin_left = 82.0
|
||||
margin_right = 263.0
|
||||
margin_bottom = 24.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 7
|
||||
color = Color( 1, 1, 1, 1 )
|
||||
edit_alpha = false
|
||||
|
||||
[node name="DirectionLabel" type="Label" parent="VBoxContainer/AnimationOptions/AnimatedOptions"]
|
||||
margin_left = 267.0
|
||||
margin_top = 5.0
|
||||
margin_right = 330.0
|
||||
margin_bottom = 19.0
|
||||
text = "Direction:"
|
||||
|
||||
[node name="Direction" type="OptionButton" parent="VBoxContainer/AnimationOptions/AnimatedOptions"]
|
||||
margin_left = 334.0
|
||||
margin_right = 516.0
|
||||
margin_bottom = 24.0
|
||||
rect_min_size = Vector2( 100, 0 )
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Forward"
|
||||
items = [ "Forward", null, false, 0, null, "Backwards", null, false, 1, null, "Ping-Pong", null, false, 2, null ]
|
||||
selected = 0
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="HSeparator2" type="HSeparator" parent="VBoxContainer"]
|
||||
margin_top = 390.0
|
||||
margin_right = 516.0
|
||||
margin_bottom = 394.0
|
||||
|
||||
[node name="Options" type="HBoxContainer" parent="VBoxContainer"]
|
||||
margin_top = 398.0
|
||||
margin_right = 516.0
|
||||
margin_bottom = 422.0
|
||||
|
||||
[node name="ResizeLabel" type="Label" parent="VBoxContainer/Options"]
|
||||
margin_top = 5.0
|
||||
margin_right = 46.0
|
||||
margin_bottom = 19.0
|
||||
rect_min_size = Vector2( 30, 0 )
|
||||
text = "Resize:"
|
||||
align = 2
|
||||
|
||||
[node name="Resize" type="SpinBox" parent="VBoxContainer/Options"]
|
||||
margin_left = 50.0
|
||||
margin_right = 235.0
|
||||
margin_bottom = 24.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
min_value = 10.0
|
||||
max_value = 1000.0
|
||||
step = 100.0
|
||||
value = 100.0
|
||||
align = 2
|
||||
suffix = "%"
|
||||
|
||||
[node name="InterpolationLabel" type="Label" parent="VBoxContainer/Options"]
|
||||
margin_left = 239.0
|
||||
margin_top = 5.0
|
||||
margin_right = 326.0
|
||||
margin_bottom = 19.0
|
||||
rect_min_size = Vector2( 30, 0 )
|
||||
text = "Interpolation:"
|
||||
align = 2
|
||||
|
||||
[node name="Interpolation" type="OptionButton" parent="VBoxContainer/Options"]
|
||||
margin_left = 330.0
|
||||
margin_right = 516.0
|
||||
margin_bottom = 24.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Nearest"
|
||||
align = 2
|
||||
items = [ "Nearest", null, false, 0, null, "Bilinear", null, false, 1, null, "Cubic", null, false, 2, null, "Trilinear", null, false, 3, null, "Lanczos", null, false, 4, null ]
|
||||
selected = 0
|
||||
|
||||
[node name="HSeparator3" type="HSeparator" parent="VBoxContainer"]
|
||||
margin_top = 426.0
|
||||
margin_right = 516.0
|
||||
margin_bottom = 430.0
|
||||
|
||||
[node name="Path" type="HBoxContainer" parent="VBoxContainer"]
|
||||
margin_top = 434.0
|
||||
margin_right = 516.0
|
||||
margin_bottom = 458.0
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer/Path"]
|
||||
margin_top = 5.0
|
||||
margin_right = 32.0
|
||||
margin_bottom = 19.0
|
||||
rect_min_size = Vector2( 30, 0 )
|
||||
text = "Path:"
|
||||
|
||||
[node name="PathLineEdit" type="LineEdit" parent="VBoxContainer/Path"]
|
||||
margin_left = 36.0
|
||||
margin_right = 453.0
|
||||
margin_bottom = 24.0
|
||||
size_flags_horizontal = 3
|
||||
align = 2
|
||||
|
||||
[node name="PathButton" type="Button" parent="VBoxContainer/Path"]
|
||||
margin_left = 457.0
|
||||
margin_right = 516.0
|
||||
margin_bottom = 24.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Browse"
|
||||
|
||||
[node name="File" type="HBoxContainer" parent="VBoxContainer"]
|
||||
margin_top = 462.0
|
||||
margin_right = 516.0
|
||||
margin_bottom = 486.0
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer/File"]
|
||||
margin_top = 5.0
|
||||
margin_right = 30.0
|
||||
margin_bottom = 19.0
|
||||
rect_min_size = Vector2( 30, 0 )
|
||||
text = "File:"
|
||||
|
||||
[node name="FileLineEdit" type="LineEdit" parent="VBoxContainer/File"]
|
||||
margin_left = 34.0
|
||||
margin_right = 376.0
|
||||
margin_bottom = 24.0
|
||||
size_flags_horizontal = 3
|
||||
align = 2
|
||||
|
||||
[node name="FileFormat" type="OptionButton" parent="VBoxContainer/File"]
|
||||
margin_left = 380.0
|
||||
margin_right = 516.0
|
||||
margin_bottom = 24.0
|
||||
rect_min_size = Vector2( 130, 0 )
|
||||
mouse_default_cursor_shape = 8
|
||||
disabled = true
|
||||
text = ".png; PNG Image"
|
||||
items = [ ".png; PNG Image", null, false, 0, null, ".gif; GIF Image", null, false, 1, null ]
|
||||
selected = 0
|
||||
|
||||
[node name="Popups" type="Node" parent="."]
|
||||
|
||||
[node name="PathDialog" type="FileDialog" parent="Popups"]
|
||||
margin_left = 8.0
|
||||
margin_top = 8.0
|
||||
margin_right = 448.0
|
||||
margin_bottom = 494.0
|
||||
rect_min_size = Vector2( 440, 300 )
|
||||
size_flags_horizontal = 0
|
||||
size_flags_vertical = 0
|
||||
window_title = "Otwórz katalog"
|
||||
resizable = true
|
||||
mode = 2
|
||||
access = 2
|
||||
current_dir = "E:/Projekty/Godot/Pixelorama"
|
||||
current_path = "E:/Projekty/Godot/Pixelorama/"
|
||||
|
||||
[node name="PathValidationAlert" type="AcceptDialog" parent="Popups"]
|
||||
margin_left = 8.0
|
||||
margin_top = 180.0
|
||||
margin_right = 448.0
|
||||
margin_bottom = 280.0
|
||||
size_flags_horizontal = 0
|
||||
size_flags_vertical = 0
|
||||
window_title = "Alarm!"
|
||||
resizable = true
|
||||
dialog_text = "Directory path or file name is not valid!"
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="FileExistsAlert" type="AcceptDialog" parent="Popups"]
|
||||
margin_left = 8.0
|
||||
margin_top = 180.0
|
||||
margin_right = 448.0
|
||||
margin_bottom = 280.0
|
||||
size_flags_horizontal = 0
|
||||
size_flags_vertical = 0
|
||||
window_title = "Alarm!"
|
||||
resizable = true
|
||||
dialog_text = "File %s already exists. Overwrite?"
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="FrameTimer" type="Timer" parent="."]
|
||||
__meta__ = {
|
||||
"_editor_description_": "Timer to advance animation frames in animation preview."
|
||||
}
|
||||
|
||||
[node name="GifExporter" type="Node" parent="."]
|
||||
script = ExtResource( 2 )
|
||||
__meta__ = {
|
||||
"_editor_description_": ""
|
||||
}
|
||||
[connection signal="about_to_show" from="." to="." method="_on_ExportDialog_about_to_show"]
|
||||
[connection signal="confirmed" from="." to="." method="_on_ExportDialog_confirmed"]
|
||||
[connection signal="custom_action" from="." to="." method="_on_ExportDialog_custom_action"]
|
||||
[connection signal="popup_hide" from="." to="." method="_on_ExportDialog_popup_hide"]
|
||||
[connection signal="tab_clicked" from="VBoxContainer/Tabs" to="." method="_on_Tabs_tab_clicked"]
|
||||
[connection signal="value_changed" from="VBoxContainer/FrameOptions/FrameNumber/FrameNumber" to="." method="_on_Frame_value_changed"]
|
||||
[connection signal="item_selected" from="VBoxContainer/SpritesheetOptions/Orientation/Orientation" to="." method="_on_Orientation_item_selected"]
|
||||
[connection signal="value_changed" from="VBoxContainer/SpritesheetOptions/Orientation/LinesCount" to="." method="_on_LinesCount_value_changed"]
|
||||
[connection signal="item_selected" from="VBoxContainer/AnimationOptions/AnimationType" to="." method="_on_AnimationType_item_selected"]
|
||||
[connection signal="toggled" from="VBoxContainer/AnimationOptions/MultipleAnimationsDirectories" to="." method="_on_MultipleAnimationsDirectories_toggled"]
|
||||
[connection signal="color_changed" from="VBoxContainer/AnimationOptions/AnimatedOptions/BackgroundColor" to="." method="_on_BackgroundColor_color_changed"]
|
||||
[connection signal="item_selected" from="VBoxContainer/AnimationOptions/AnimatedOptions/Direction" to="." method="_on_Direction_item_selected"]
|
||||
[connection signal="value_changed" from="VBoxContainer/Options/Resize" to="." method="_on_Resize_value_changed"]
|
||||
[connection signal="item_selected" from="VBoxContainer/Options/Interpolation" to="." method="_on_Interpolation_item_selected"]
|
||||
[connection signal="text_changed" from="VBoxContainer/Path/PathLineEdit" to="." method="_on_PathLineEdit_text_changed"]
|
||||
[connection signal="pressed" from="VBoxContainer/Path/PathButton" to="." method="_on_PathButton_pressed"]
|
||||
[connection signal="text_changed" from="VBoxContainer/File/FileLineEdit" to="." method="_on_FileLineEdit_text_changed"]
|
||||
[connection signal="item_selected" from="VBoxContainer/File/FileFormat" to="." method="_on_FileFormat_item_selected"]
|
||||
[connection signal="dir_selected" from="Popups/PathDialog" to="." method="_on_FileDialog_dir_selected"]
|
||||
[connection signal="confirmed" from="Popups/FileExistsAlert" to="." method="_on_FileExistsAlert_confirmed"]
|
||||
[connection signal="custom_action" from="Popups/FileExistsAlert" to="." method="_on_FileExistsAlert_custom_action"]
|
||||
[connection signal="timeout" from="FrameTimer" to="." method="_on_FrameTimer_timeout"]
|
134
src/Dialogs/FrameTagDialog.gd
Normal file
134
src/Dialogs/FrameTagDialog.gd
Normal file
|
@ -0,0 +1,134 @@
|
|||
extends AcceptDialog
|
||||
|
||||
|
||||
var current_tag_id := 0
|
||||
var tag_vboxes := []
|
||||
var delete_tag_button : Button
|
||||
|
||||
onready var main_vbox_cont : VBoxContainer = $VBoxContainer/ScrollContainer/VBoxTagContainer
|
||||
onready var add_tag_button : TextureButton = $VBoxContainer/ScrollContainer/VBoxTagContainer/AddTag
|
||||
onready var options_dialog = $TagOptions
|
||||
|
||||
|
||||
func _on_FrameTagDialog_about_to_show() -> void:
|
||||
Global.can_draw = false
|
||||
for vbox in tag_vboxes:
|
||||
vbox.queue_free()
|
||||
tag_vboxes.clear()
|
||||
|
||||
var i := 0
|
||||
for tag in Global.animation_tags:
|
||||
var vbox_cont := VBoxContainer.new()
|
||||
var hbox_cont := HBoxContainer.new()
|
||||
var tag_label := Label.new()
|
||||
if tag[2] == tag[3]:
|
||||
tag_label.text = "Tag %s (Frame %s)" % [i + 1, tag[2]]
|
||||
else:
|
||||
tag_label.text = "Tag %s (Frames %s-%s)" % [i + 1, tag[2], tag[3]]
|
||||
hbox_cont.add_child(tag_label)
|
||||
|
||||
var edit_button := Button.new()
|
||||
edit_button.text = "Edit"
|
||||
edit_button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND
|
||||
edit_button.connect("pressed", self, "_on_EditButton_pressed", [i])
|
||||
hbox_cont.add_child(edit_button)
|
||||
vbox_cont.add_child(hbox_cont)
|
||||
|
||||
var name_label := Label.new()
|
||||
name_label.text = tag[0]
|
||||
name_label.modulate = tag[1]
|
||||
vbox_cont.add_child(name_label)
|
||||
|
||||
var hsep := HSeparator.new()
|
||||
hsep.size_flags_horizontal = SIZE_EXPAND_FILL
|
||||
vbox_cont.add_child(hsep)
|
||||
|
||||
main_vbox_cont.add_child(vbox_cont)
|
||||
tag_vboxes.append(vbox_cont)
|
||||
|
||||
i += 1
|
||||
|
||||
add_tag_button.visible = true
|
||||
main_vbox_cont.move_child(add_tag_button, main_vbox_cont.get_child_count() - 1)
|
||||
|
||||
|
||||
func _on_FrameTagDialog_popup_hide() -> void:
|
||||
Global.can_draw = true
|
||||
|
||||
|
||||
func _on_AddTag_pressed() -> void:
|
||||
options_dialog.popup_centered()
|
||||
current_tag_id = Global.animation_tags.size()
|
||||
options_dialog.get_node("GridContainer/FromSpinBox").value = Global.current_frame + 1
|
||||
options_dialog.get_node("GridContainer/ToSpinBox").value = Global.current_frame + 1
|
||||
|
||||
|
||||
func _on_EditButton_pressed(_tag_id : int) -> void:
|
||||
options_dialog.popup_centered()
|
||||
current_tag_id = _tag_id
|
||||
options_dialog.get_node("GridContainer/NameLineEdit").text = Global.animation_tags[_tag_id][0]
|
||||
options_dialog.get_node("GridContainer/ColorPickerButton").color = Global.animation_tags[_tag_id][1]
|
||||
options_dialog.get_node("GridContainer/FromSpinBox").value = Global.animation_tags[_tag_id][2]
|
||||
options_dialog.get_node("GridContainer/ToSpinBox").value = Global.animation_tags[_tag_id][3]
|
||||
if !delete_tag_button:
|
||||
delete_tag_button = options_dialog.add_button("Delete Tag", true, "delete_tag")
|
||||
else:
|
||||
delete_tag_button.visible = true
|
||||
|
||||
|
||||
func _on_TagOptions_confirmed() -> void:
|
||||
var tag_name : String = options_dialog.get_node("GridContainer/NameLineEdit").text
|
||||
var tag_color : Color = options_dialog.get_node("GridContainer/ColorPickerButton").color
|
||||
var tag_from : int = options_dialog.get_node("GridContainer/FromSpinBox").value
|
||||
var tag_to : int = options_dialog.get_node("GridContainer/ToSpinBox").value
|
||||
|
||||
if tag_to > Global.canvases.size():
|
||||
tag_to = Global.canvases.size()
|
||||
|
||||
if tag_from > tag_to:
|
||||
tag_from = tag_to
|
||||
|
||||
var new_animation_tags := Global.animation_tags.duplicate(true)
|
||||
if current_tag_id == Global.animation_tags.size():
|
||||
new_animation_tags.append([tag_name, tag_color, tag_from, tag_to])
|
||||
else:
|
||||
new_animation_tags[current_tag_id][0] = tag_name
|
||||
new_animation_tags[current_tag_id][1] = tag_color
|
||||
new_animation_tags[current_tag_id][2] = tag_from
|
||||
new_animation_tags[current_tag_id][3] = tag_to
|
||||
|
||||
# Handle Undo/Redo
|
||||
Global.undos += 1
|
||||
Global.undo_redo.create_action("Modify Frame Tag")
|
||||
Global.undo_redo.add_do_method(Global, "general_redo")
|
||||
Global.undo_redo.add_undo_method(Global, "general_undo")
|
||||
Global.undo_redo.add_do_property(Global, "animation_tags", new_animation_tags)
|
||||
Global.undo_redo.add_undo_property(Global, "animation_tags", Global.animation_tags)
|
||||
Global.undo_redo.commit_action()
|
||||
_on_FrameTagDialog_about_to_show()
|
||||
|
||||
|
||||
func _on_TagOptions_custom_action(action : String) -> void:
|
||||
if action == "delete_tag":
|
||||
var new_animation_tags := Global.animation_tags.duplicate(true)
|
||||
new_animation_tags.remove(current_tag_id)
|
||||
# Handle Undo/Redo
|
||||
Global.undos += 1
|
||||
Global.undo_redo.create_action("Delete Frame Tag")
|
||||
Global.undo_redo.add_do_method(Global, "general_redo")
|
||||
Global.undo_redo.add_undo_method(Global, "general_undo")
|
||||
Global.undo_redo.add_do_property(Global, "animation_tags", new_animation_tags)
|
||||
Global.undo_redo.add_undo_property(Global, "animation_tags", Global.animation_tags)
|
||||
Global.undo_redo.commit_action()
|
||||
|
||||
options_dialog.hide()
|
||||
_on_FrameTagDialog_about_to_show()
|
||||
|
||||
|
||||
func _on_TagOptions_popup_hide() -> void:
|
||||
if delete_tag_button:
|
||||
delete_tag_button.visible = false
|
||||
|
||||
|
||||
func _on_PlayOnlyTags_toggled(button_pressed : bool) -> void:
|
||||
Global.play_only_tags = button_pressed
|
169
src/Dialogs/FrameTagDialog.tscn
Normal file
169
src/Dialogs/FrameTagDialog.tscn
Normal file
|
@ -0,0 +1,169 @@
|
|||
[gd_scene load_steps=3 format=2]
|
||||
|
||||
[ext_resource path="res://src/Dialogs/FrameTagDialog.gd" type="Script" id=1]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Timeline/new_frame.png" type="Texture" id=2]
|
||||
|
||||
|
||||
[node name="FrameTagDialog" type="AcceptDialog"]
|
||||
margin_right = 83.0
|
||||
margin_bottom = 58.0
|
||||
rect_min_size = Vector2( 400, 200 )
|
||||
window_title = "Frame Tag Properties"
|
||||
resizable = true
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
margin_left = 8.0
|
||||
margin_top = 8.0
|
||||
margin_right = -8.0
|
||||
margin_bottom = -36.0
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="VBoxContainer"]
|
||||
margin_right = 384.0
|
||||
margin_bottom = 128.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="VBoxTagContainer" type="VBoxContainer" parent="VBoxContainer/ScrollContainer"]
|
||||
margin_right = 384.0
|
||||
margin_bottom = 28.0
|
||||
size_flags_horizontal = 3
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="VBoxContainer/ScrollContainer/VBoxTagContainer"]
|
||||
margin_right = 384.0
|
||||
margin_bottom = 4.0
|
||||
size_flags_horizontal = 3
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="AddTag" type="Button" parent="VBoxContainer/ScrollContainer/VBoxTagContainer" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_top = 8.0
|
||||
margin_right = 20.0
|
||||
margin_bottom = 28.0
|
||||
rect_min_size = Vector2( 20, 0 )
|
||||
hint_tooltip = "Add a new frame tag"
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 0
|
||||
size_flags_vertical = 0
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="VBoxContainer/ScrollContainer/VBoxTagContainer/AddTag"]
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
margin_left = -6.0
|
||||
margin_top = -6.0
|
||||
margin_right = 6.0
|
||||
margin_bottom = 6.0
|
||||
texture = ExtResource( 2 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="PlayOnlyTags" type="CheckBox" parent="VBoxContainer"]
|
||||
margin_top = 132.0
|
||||
margin_right = 333.0
|
||||
margin_bottom = 156.0
|
||||
hint_tooltip = "If it's selected, the animation plays only on the frames that have the same tag.
|
||||
If it's not, the animation will play for all frames, ignoring tags."
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 0
|
||||
pressed = true
|
||||
text = "Animation plays only on frames of the same tag"
|
||||
|
||||
[node name="TagOptions" type="ConfirmationDialog" parent="."]
|
||||
margin_left = 8.0
|
||||
margin_top = 8.0
|
||||
margin_right = 392.0
|
||||
margin_bottom = 164.0
|
||||
|
||||
[node name="GridContainer" type="GridContainer" parent="TagOptions"]
|
||||
margin_left = 8.0
|
||||
margin_top = 8.0
|
||||
margin_right = 376.0
|
||||
margin_bottom = 120.0
|
||||
custom_constants/vseparation = 8
|
||||
custom_constants/hseparation = 8
|
||||
columns = 4
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="NameLabel" type="Label" parent="TagOptions/GridContainer"]
|
||||
margin_top = 5.0
|
||||
margin_right = 42.0
|
||||
margin_bottom = 19.0
|
||||
text = "Name:"
|
||||
|
||||
[node name="NameLineEdit" type="LineEdit" parent="TagOptions/GridContainer"]
|
||||
margin_left = 50.0
|
||||
margin_right = 124.0
|
||||
margin_bottom = 24.0
|
||||
caret_blink = true
|
||||
caret_blink_speed = 0.5
|
||||
|
||||
[node name="ColorLabel" type="Label" parent="TagOptions/GridContainer"]
|
||||
margin_left = 132.0
|
||||
margin_top = 5.0
|
||||
margin_right = 169.0
|
||||
margin_bottom = 19.0
|
||||
text = "Color:"
|
||||
|
||||
[node name="ColorPickerButton" type="ColorPickerButton" parent="TagOptions/GridContainer"]
|
||||
margin_left = 177.0
|
||||
margin_right = 251.0
|
||||
margin_bottom = 24.0
|
||||
mouse_default_cursor_shape = 2
|
||||
color = Color( 1, 0, 0, 1 )
|
||||
|
||||
[node name="FromLabel" type="Label" parent="TagOptions/GridContainer"]
|
||||
margin_top = 37.0
|
||||
margin_right = 42.0
|
||||
margin_bottom = 51.0
|
||||
text = "From:"
|
||||
|
||||
[node name="FromSpinBox" type="SpinBox" parent="TagOptions/GridContainer"]
|
||||
margin_left = 50.0
|
||||
margin_top = 32.0
|
||||
margin_right = 124.0
|
||||
margin_bottom = 56.0
|
||||
mouse_default_cursor_shape = 2
|
||||
min_value = 1.0
|
||||
value = 1.0
|
||||
|
||||
[node name="ToLabel" type="Label" parent="TagOptions/GridContainer"]
|
||||
margin_left = 132.0
|
||||
margin_top = 37.0
|
||||
margin_right = 169.0
|
||||
margin_bottom = 51.0
|
||||
text = "To:"
|
||||
|
||||
[node name="ToSpinBox" type="SpinBox" parent="TagOptions/GridContainer"]
|
||||
margin_left = 177.0
|
||||
margin_top = 32.0
|
||||
margin_right = 251.0
|
||||
margin_bottom = 56.0
|
||||
mouse_default_cursor_shape = 2
|
||||
min_value = 1.0
|
||||
value = 1.0
|
||||
[connection signal="about_to_show" from="." to="." method="_on_FrameTagDialog_about_to_show"]
|
||||
[connection signal="popup_hide" from="." to="." method="_on_FrameTagDialog_popup_hide"]
|
||||
[connection signal="pressed" from="VBoxContainer/ScrollContainer/VBoxTagContainer/AddTag" to="." method="_on_AddTag_pressed"]
|
||||
[connection signal="toggled" from="VBoxContainer/PlayOnlyTags" to="." method="_on_PlayOnlyTags_toggled"]
|
||||
[connection signal="confirmed" from="TagOptions" to="." method="_on_TagOptions_confirmed"]
|
||||
[connection signal="custom_action" from="TagOptions" to="." method="_on_TagOptions_custom_action"]
|
||||
[connection signal="popup_hide" from="TagOptions" to="." method="_on_TagOptions_popup_hide"]
|
100
src/Dialogs/HSVDialog.gd
Normal file
100
src/Dialogs/HSVDialog.gd
Normal file
|
@ -0,0 +1,100 @@
|
|||
extends WindowDialog
|
||||
|
||||
var current_layer : Image
|
||||
var preview_image : Image
|
||||
var preview_texture : ImageTexture
|
||||
|
||||
onready var hue_slider = $MarginContainer/VBoxContainer/HBoxContainer/Sliders/Hue
|
||||
onready var sat_slider = $MarginContainer/VBoxContainer/HBoxContainer/Sliders/Saturation
|
||||
onready var val_slider = $MarginContainer/VBoxContainer/HBoxContainer/Sliders/Value
|
||||
|
||||
onready var hue_spinbox = $MarginContainer/VBoxContainer/HBoxContainer/TextBoxes/Hue
|
||||
onready var sat_spinbox = $MarginContainer/VBoxContainer/HBoxContainer/TextBoxes/Saturation
|
||||
onready var val_spinbox = $MarginContainer/VBoxContainer/HBoxContainer/TextBoxes/Value
|
||||
|
||||
onready var preview = $MarginContainer/VBoxContainer/TextureRect
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
current_layer = Image.new()
|
||||
preview_image = Image.new()
|
||||
preview_texture = ImageTexture.new()
|
||||
preview_texture.flags = 0
|
||||
|
||||
|
||||
func _on_HSVDialog_about_to_show() -> void:
|
||||
current_layer = Global.canvas.layers[Global.current_layer][0]
|
||||
preview_image.copy_from(current_layer)
|
||||
update_preview()
|
||||
|
||||
|
||||
func _on_Cancel_pressed() -> void:
|
||||
visible = false
|
||||
reset()
|
||||
|
||||
|
||||
func _on_Apply_pressed() -> void:
|
||||
Global.canvas.handle_undo("Draw")
|
||||
Global.canvas.adjust_hsv(current_layer,0,hue_slider.value)
|
||||
Global.canvas.adjust_hsv(current_layer,1,sat_slider.value)
|
||||
Global.canvas.adjust_hsv(current_layer,2,val_slider.value)
|
||||
Global.canvas.update_texture(Global.current_layer)
|
||||
Global.canvas.handle_redo("Draw")
|
||||
reset()
|
||||
visible = false
|
||||
|
||||
|
||||
func reset() -> void:
|
||||
disconnect_signals()
|
||||
hue_slider.value = 0
|
||||
sat_slider.value = 0
|
||||
val_slider.value = 0
|
||||
hue_spinbox.value = 0
|
||||
sat_spinbox.value = 0
|
||||
val_spinbox.value = 0
|
||||
reconnect_signals()
|
||||
|
||||
|
||||
func update_preview() -> void:
|
||||
preview_image.copy_from(current_layer)
|
||||
Global.canvas.adjust_hsv(preview_image,0,hue_slider.value)
|
||||
Global.canvas.adjust_hsv(preview_image,1,sat_slider.value)
|
||||
Global.canvas.adjust_hsv(preview_image,2,val_slider.value)
|
||||
preview_texture.create_from_image(preview_image, 0)
|
||||
preview.texture = preview_texture
|
||||
|
||||
|
||||
func disconnect_signals() -> void:
|
||||
hue_slider.disconnect("value_changed",self,"_on_Hue_value_changed")
|
||||
sat_slider.disconnect("value_changed",self,"_on_Saturation_value_changed")
|
||||
val_slider.disconnect("value_changed",self,"_on_Value_value_changed")
|
||||
hue_spinbox.disconnect("value_changed",self,"_on_Hue_value_changed")
|
||||
sat_spinbox.disconnect("value_changed",self,"_on_Saturation_value_changed")
|
||||
val_spinbox.disconnect("value_changed",self,"_on_Value_value_changed")
|
||||
|
||||
|
||||
func reconnect_signals() -> void:
|
||||
hue_slider.connect("value_changed",self,"_on_Hue_value_changed")
|
||||
sat_slider.connect("value_changed",self,"_on_Saturation_value_changed")
|
||||
val_slider.connect("value_changed",self,"_on_Value_value_changed")
|
||||
hue_spinbox.connect("value_changed",self,"_on_Hue_value_changed")
|
||||
sat_spinbox.connect("value_changed",self,"_on_Saturation_value_changed")
|
||||
val_spinbox.connect("value_changed",self,"_on_Value_value_changed")
|
||||
|
||||
|
||||
func _on_Hue_value_changed(value : float) -> void:
|
||||
hue_spinbox.value = value
|
||||
hue_slider.value = value
|
||||
update_preview()
|
||||
|
||||
|
||||
func _on_Saturation_value_changed(value : float) -> void:
|
||||
sat_spinbox.value = value
|
||||
sat_slider.value = value
|
||||
update_preview()
|
||||
|
||||
|
||||
func _on_Value_value_changed(value : float) -> void:
|
||||
val_spinbox.value = value
|
||||
val_slider.value = value
|
||||
update_preview()
|
167
src/Dialogs/HSVDialog.tscn
Normal file
167
src/Dialogs/HSVDialog.tscn
Normal file
|
@ -0,0 +1,167 @@
|
|||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://src/Dialogs/HSVDialog.gd" type="Script" id=1]
|
||||
|
||||
|
||||
[node name="HSVDialog" type="WindowDialog"]
|
||||
margin_left = 1.0
|
||||
margin_top = -1.0
|
||||
margin_right = 464.0
|
||||
margin_bottom = 318.0
|
||||
window_title = "Adjust HSV"
|
||||
resizable = true
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
custom_constants/margin_right = 5
|
||||
custom_constants/margin_top = 5
|
||||
custom_constants/margin_left = 5
|
||||
custom_constants/margin_bottom = 5
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
|
||||
margin_left = 5.0
|
||||
margin_top = 5.0
|
||||
margin_right = 458.0
|
||||
margin_bottom = 314.0
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="MarginContainer/VBoxContainer"]
|
||||
margin_right = 453.0
|
||||
margin_bottom = 197.0
|
||||
size_flags_vertical = 3
|
||||
expand = true
|
||||
stretch_mode = 6
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"]
|
||||
margin_top = 201.0
|
||||
margin_right = 453.0
|
||||
margin_bottom = 285.0
|
||||
custom_constants/separation = 10
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Names" type="VBoxContainer" parent="MarginContainer/VBoxContainer/HBoxContainer"]
|
||||
margin_right = 82.0
|
||||
margin_bottom = 84.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 0.9
|
||||
custom_constants/separation = 8
|
||||
|
||||
[node name="Hue" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer/Names"]
|
||||
margin_right = 82.0
|
||||
margin_bottom = 14.0
|
||||
text = "Hue"
|
||||
align = 2
|
||||
|
||||
[node name="Saturation" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer/Names"]
|
||||
margin_top = 22.0
|
||||
margin_right = 82.0
|
||||
margin_bottom = 36.0
|
||||
text = "Saturation"
|
||||
align = 2
|
||||
|
||||
[node name="Value" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer/Names"]
|
||||
margin_top = 44.0
|
||||
margin_right = 82.0
|
||||
margin_bottom = 58.0
|
||||
text = "Value"
|
||||
align = 2
|
||||
|
||||
[node name="Sliders" type="VBoxContainer" parent="MarginContainer/VBoxContainer/HBoxContainer"]
|
||||
margin_left = 92.0
|
||||
margin_right = 368.0
|
||||
margin_bottom = 84.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 3.0
|
||||
custom_constants/separation = 7
|
||||
|
||||
[node name="Hue" type="HSlider" parent="MarginContainer/VBoxContainer/HBoxContainer/Sliders"]
|
||||
margin_right = 276.0
|
||||
margin_bottom = 16.0
|
||||
mouse_default_cursor_shape = 2
|
||||
min_value = -180.0
|
||||
max_value = 180.0
|
||||
|
||||
[node name="Saturation" type="HSlider" parent="MarginContainer/VBoxContainer/HBoxContainer/Sliders"]
|
||||
margin_top = 23.0
|
||||
margin_right = 276.0
|
||||
margin_bottom = 39.0
|
||||
mouse_default_cursor_shape = 2
|
||||
min_value = -100.0
|
||||
|
||||
[node name="Value" type="HSlider" parent="MarginContainer/VBoxContainer/HBoxContainer/Sliders"]
|
||||
margin_top = 46.0
|
||||
margin_right = 276.0
|
||||
margin_bottom = 62.0
|
||||
mouse_default_cursor_shape = 2
|
||||
min_value = -100.0
|
||||
|
||||
[node name="TextBoxes" type="VBoxContainer" parent="MarginContainer/VBoxContainer/HBoxContainer"]
|
||||
margin_left = 378.0
|
||||
margin_right = 452.0
|
||||
margin_bottom = 84.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 0.0
|
||||
custom_constants/separation = 6
|
||||
|
||||
[node name="Hue" type="SpinBox" parent="MarginContainer/VBoxContainer/HBoxContainer/TextBoxes"]
|
||||
margin_right = 74.0
|
||||
margin_bottom = 24.0
|
||||
mouse_default_cursor_shape = 1
|
||||
min_value = -180.0
|
||||
max_value = 180.0
|
||||
|
||||
[node name="Saturation" type="SpinBox" parent="MarginContainer/VBoxContainer/HBoxContainer/TextBoxes"]
|
||||
margin_top = 30.0
|
||||
margin_right = 74.0
|
||||
margin_bottom = 54.0
|
||||
mouse_default_cursor_shape = 1
|
||||
min_value = -100.0
|
||||
|
||||
[node name="Value" type="SpinBox" parent="MarginContainer/VBoxContainer/HBoxContainer/TextBoxes"]
|
||||
margin_top = 60.0
|
||||
margin_right = 74.0
|
||||
margin_bottom = 84.0
|
||||
mouse_default_cursor_shape = 1
|
||||
min_value = -100.0
|
||||
|
||||
[node name="HBoxContainer2" type="HBoxContainer" parent="MarginContainer/VBoxContainer"]
|
||||
margin_top = 289.0
|
||||
margin_right = 453.0
|
||||
margin_bottom = 309.0
|
||||
custom_constants/separation = 16
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Apply" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer2"]
|
||||
margin_right = 218.0
|
||||
margin_bottom = 20.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Apply"
|
||||
|
||||
[node name="Cancel" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer2"]
|
||||
margin_left = 234.0
|
||||
margin_right = 453.0
|
||||
margin_bottom = 20.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Cancel"
|
||||
[connection signal="about_to_show" from="." to="." method="_on_HSVDialog_about_to_show"]
|
||||
[connection signal="value_changed" from="MarginContainer/VBoxContainer/HBoxContainer/Sliders/Hue" to="." method="_on_Hue_value_changed"]
|
||||
[connection signal="value_changed" from="MarginContainer/VBoxContainer/HBoxContainer/Sliders/Saturation" to="." method="_on_Saturation_value_changed"]
|
||||
[connection signal="value_changed" from="MarginContainer/VBoxContainer/HBoxContainer/Sliders/Value" to="." method="_on_Value_value_changed"]
|
||||
[connection signal="value_changed" from="MarginContainer/VBoxContainer/HBoxContainer/TextBoxes/Hue" to="." method="_on_Hue_value_changed"]
|
||||
[connection signal="value_changed" from="MarginContainer/VBoxContainer/HBoxContainer/TextBoxes/Saturation" to="." method="_on_Saturation_value_changed"]
|
||||
[connection signal="value_changed" from="MarginContainer/VBoxContainer/HBoxContainer/TextBoxes/Value" to="." method="_on_Value_value_changed"]
|
||||
[connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer2/Apply" to="." method="_on_Apply_pressed"]
|
||||
[connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer2/Cancel" to="." method="_on_Cancel_pressed"]
|
146
src/Dialogs/ImportSprites.gd
Normal file
146
src/Dialogs/ImportSprites.gd
Normal file
|
@ -0,0 +1,146 @@
|
|||
extends FileDialog
|
||||
|
||||
var new_frame := true
|
||||
var import_spritesheet := false
|
||||
var spritesheet_horizontal := 1
|
||||
var spritesheet_vertical := 1
|
||||
|
||||
func _ready() -> void:
|
||||
var children := []
|
||||
for i in range(get_child_count()):
|
||||
if i > 7:
|
||||
children.append(get_child(i))
|
||||
|
||||
for child in children:
|
||||
remove_child(child)
|
||||
get_vbox().add_child(child)
|
||||
|
||||
|
||||
func _on_ImportAsNewFrame_pressed() -> void:
|
||||
new_frame = !new_frame
|
||||
|
||||
|
||||
func _on_ImportSpritesheet_pressed() -> void:
|
||||
import_spritesheet = !import_spritesheet
|
||||
var spritesheet_container = Global.find_node_by_name(self, "Spritesheet")
|
||||
spritesheet_container.visible = import_spritesheet
|
||||
|
||||
|
||||
func _on_HorizontalFrames_value_changed(value) -> void:
|
||||
spritesheet_horizontal = value
|
||||
|
||||
|
||||
func _on_VerticalFrames_value_changed(value) -> void:
|
||||
spritesheet_vertical = value
|
||||
|
||||
|
||||
func _on_ImportSprites_files_selected(paths : PoolStringArray) -> void:
|
||||
Global.control.opensprite_file_selected = true
|
||||
if !new_frame: # If we're not adding a new frame, delete the previous
|
||||
Global.clear_canvases()
|
||||
|
||||
var first_path : String = paths[0]
|
||||
var i : int = Global.canvases.size()
|
||||
if !import_spritesheet:
|
||||
# Find the biggest image and let it handle the camera zoom options
|
||||
var max_size : Vector2
|
||||
var biggest_canvas : Canvas
|
||||
for path in paths:
|
||||
var image := Image.new()
|
||||
var err := image.load(path)
|
||||
if err != OK: # An error occured
|
||||
var file_name : String = path.get_file()
|
||||
Global.error_dialog.set_text(tr("Can't load file '%s'.\nError code: %s") % [file_name, str(err)])
|
||||
Global.error_dialog.popup_centered()
|
||||
continue
|
||||
|
||||
var canvas : Canvas = load("res://src/Canvas.tscn").instance()
|
||||
canvas.size = image.get_size()
|
||||
image.convert(Image.FORMAT_RGBA8)
|
||||
image.lock()
|
||||
var tex := ImageTexture.new()
|
||||
tex.create_from_image(image, 0)
|
||||
# Store [Image, ImageTexture, Opacity]
|
||||
canvas.layers.append([image, tex, 1])
|
||||
|
||||
for _i in range(1, Global.layers.size()):
|
||||
var empty_sprite := Image.new()
|
||||
empty_sprite.create(canvas.size.x, canvas.size.y, false, Image.FORMAT_RGBA8)
|
||||
empty_sprite.fill(Color(0, 0, 0, 0))
|
||||
empty_sprite.lock()
|
||||
|
||||
var empty_tex := ImageTexture.new()
|
||||
empty_tex.create_from_image(empty_sprite, 0)
|
||||
|
||||
# Store [Image, ImageTexture, Opacity]
|
||||
canvas.layers.append([empty_sprite, empty_tex, 1])
|
||||
|
||||
canvas.frame = i
|
||||
Global.canvases.append(canvas)
|
||||
Global.canvas_parent.add_child(canvas)
|
||||
canvas.visible = false
|
||||
if path == paths[0]: # If it's the first file
|
||||
max_size = canvas.size
|
||||
biggest_canvas = canvas
|
||||
else:
|
||||
if canvas.size > max_size:
|
||||
biggest_canvas = canvas
|
||||
|
||||
i += 1
|
||||
|
||||
if biggest_canvas:
|
||||
biggest_canvas.camera_zoom()
|
||||
|
||||
else:
|
||||
var image := Image.new()
|
||||
var err := image.load(first_path)
|
||||
if err != OK: # An error occured
|
||||
var file_name : String = first_path.get_file()
|
||||
Global.error_dialog.set_text(tr("Can't load file '%s'.\nError code: %s") % [file_name, str(err)])
|
||||
Global.error_dialog.popup_centered()
|
||||
return
|
||||
|
||||
spritesheet_horizontal = min(spritesheet_horizontal, image.get_size().x)
|
||||
spritesheet_vertical = min(spritesheet_vertical, image.get_size().y)
|
||||
var frame_width := image.get_size().x / spritesheet_horizontal
|
||||
var frame_height := image.get_size().y / spritesheet_vertical
|
||||
for yy in range(spritesheet_vertical):
|
||||
for xx in range(spritesheet_horizontal):
|
||||
var canvas : Canvas = load("res://src/Canvas.tscn").instance()
|
||||
var cropped_image := Image.new()
|
||||
cropped_image = image.get_rect(Rect2(frame_width * xx, frame_height * yy, frame_width, frame_height))
|
||||
canvas.size = cropped_image.get_size()
|
||||
cropped_image.convert(Image.FORMAT_RGBA8)
|
||||
cropped_image.lock()
|
||||
var tex := ImageTexture.new()
|
||||
tex.create_from_image(cropped_image, 0)
|
||||
# Store [Image, ImageTexture, Opacity]
|
||||
canvas.layers.append([cropped_image, tex, 1])
|
||||
for _i in range(1, Global.layers.size()):
|
||||
var empty_sprite := Image.new()
|
||||
empty_sprite.create(canvas.size.x, canvas.size.y, false, Image.FORMAT_RGBA8)
|
||||
empty_sprite.fill(Color(0, 0, 0, 0))
|
||||
empty_sprite.lock()
|
||||
|
||||
var empty_tex := ImageTexture.new()
|
||||
empty_tex.create_from_image(empty_sprite, 0)
|
||||
|
||||
# Store [Image, ImageTexture, Opacity]
|
||||
canvas.layers.append([empty_sprite, empty_tex, 1])
|
||||
|
||||
canvas.frame = i
|
||||
Global.canvases.append(canvas)
|
||||
Global.canvas_parent.add_child(canvas)
|
||||
canvas.visible = false
|
||||
|
||||
i += 1
|
||||
|
||||
Global.canvases[Global.canvases.size() - 1].camera_zoom()
|
||||
|
||||
Global.canvases = Global.canvases # Just to call Global.canvases_changed
|
||||
Global.current_frame = i - 1
|
||||
Global.canvas = Global.canvases[Global.canvases.size() - 1]
|
||||
Global.canvas.visible = true
|
||||
|
||||
Global.window_title = first_path.get_file() + " (" + tr("imported") + ") - Pixelorama"
|
||||
|
77
src/Dialogs/ImportSprites.tscn
Normal file
77
src/Dialogs/ImportSprites.tscn
Normal file
|
@ -0,0 +1,77 @@
|
|||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://src/Dialogs/ImportSprites.gd" type="Script" id=1]
|
||||
|
||||
|
||||
[node name="ImportSprites" type="FileDialog"]
|
||||
margin_right = 515.0
|
||||
margin_bottom = 348.0
|
||||
window_title = "Open File(s)"
|
||||
resizable = true
|
||||
mode = 1
|
||||
access = 2
|
||||
filters = PoolStringArray( "*.bmp ; BMP Image", "*.hdr ; Radiance HDR Image", "*.jpg,*.jpeg ; JPEG Image", "*.png ; PNG Image", "*.svg ; SVG Image", "*.tga ; TGA Image", "*.webp ; WebP Image" )
|
||||
current_dir = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama"
|
||||
current_path = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama/"
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="HBoxContainer2" type="HBoxContainer" parent="."]
|
||||
margin_left = 8.0
|
||||
margin_top = 8.0
|
||||
margin_right = 507.0
|
||||
margin_bottom = 312.0
|
||||
|
||||
[node name="ImportAsNewFrame" type="CheckBox" parent="HBoxContainer2"]
|
||||
margin_right = 161.0
|
||||
margin_bottom = 304.0
|
||||
mouse_default_cursor_shape = 2
|
||||
pressed = true
|
||||
text = "Import as new frame"
|
||||
|
||||
[node name="ImportSpritesheet" type="CheckBox" parent="HBoxContainer2"]
|
||||
margin_left = 165.0
|
||||
margin_right = 343.0
|
||||
margin_bottom = 304.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Import as a spritesheet"
|
||||
|
||||
[node name="Spritesheet" type="HBoxContainer" parent="."]
|
||||
visible = false
|
||||
margin_left = 8.0
|
||||
margin_top = 8.0
|
||||
margin_right = 507.0
|
||||
margin_bottom = 312.0
|
||||
|
||||
[node name="Label" type="Label" parent="Spritesheet"]
|
||||
margin_top = 1.0
|
||||
margin_right = 101.0
|
||||
margin_bottom = 16.0
|
||||
text = "Horizontal frames:"
|
||||
|
||||
[node name="HorizontalFrames" type="SpinBox" parent="Spritesheet"]
|
||||
margin_left = 105.0
|
||||
margin_right = 159.0
|
||||
margin_bottom = 17.0
|
||||
mouse_default_cursor_shape = 2
|
||||
min_value = 1.0
|
||||
value = 1.0
|
||||
|
||||
[node name="Label2" type="Label" parent="Spritesheet"]
|
||||
margin_left = 163.0
|
||||
margin_top = 1.0
|
||||
margin_right = 248.0
|
||||
margin_bottom = 16.0
|
||||
text = "Vertical frames:"
|
||||
|
||||
[node name="VerticalFrames" type="SpinBox" parent="Spritesheet"]
|
||||
margin_left = 252.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 17.0
|
||||
mouse_default_cursor_shape = 2
|
||||
min_value = 1.0
|
||||
value = 1.0
|
||||
[connection signal="files_selected" from="." to="." method="_on_ImportSprites_files_selected"]
|
||||
[connection signal="pressed" from="HBoxContainer2/ImportAsNewFrame" to="." method="_on_ImportAsNewFrame_pressed"]
|
||||
[connection signal="pressed" from="HBoxContainer2/ImportSpritesheet" to="." method="_on_ImportSpritesheet_pressed"]
|
||||
[connection signal="value_changed" from="Spritesheet/HorizontalFrames" to="." method="_on_HorizontalFrames_value_changed"]
|
||||
[connection signal="value_changed" from="Spritesheet/VerticalFrames" to="." method="_on_VerticalFrames_value_changed"]
|
7
src/Dialogs/NoProjectEditedOrCreatedAlertDialog.tscn
Normal file
7
src/Dialogs/NoProjectEditedOrCreatedAlertDialog.tscn
Normal file
|
@ -0,0 +1,7 @@
|
|||
[gd_scene format=2]
|
||||
|
||||
[node name="NoProjectEditedOrCreatedAlertDialog" type="AcceptDialog"]
|
||||
dialog_text = "You haven't saved or opened any project in Pixelorama yet!"
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
7
src/Dialogs/OpenLastProjectAlertDialog.tscn
Normal file
7
src/Dialogs/OpenLastProjectAlertDialog.tscn
Normal file
|
@ -0,0 +1,7 @@
|
|||
[gd_scene format=2]
|
||||
|
||||
[node name="OpenLastProjectAlertDialog" type="AcceptDialog"]
|
||||
margin_right = 209.0
|
||||
margin_bottom = 58.0
|
||||
window_title = "Alarm!"
|
||||
dialog_text = "Cannot find last project file."
|
146
src/Dialogs/OutlineDialog.gd
Normal file
146
src/Dialogs/OutlineDialog.gd
Normal file
|
@ -0,0 +1,146 @@
|
|||
extends ConfirmationDialog
|
||||
|
||||
func _ready() -> void:
|
||||
$OptionsContainer/OutlineColor.get_picker().presets_visible = false
|
||||
|
||||
|
||||
func _on_OutlineDialog_confirmed() -> void:
|
||||
var outline_color : Color = $OptionsContainer/OutlineColor.color
|
||||
var thickness : int = $OptionsContainer/ThickValue.value
|
||||
var diagonal : bool = $OptionsContainer/DiagonalCheckBox.pressed
|
||||
var inside_image : bool = $OptionsContainer/InsideImageCheckBox.pressed
|
||||
|
||||
var image : Image = Global.canvas.layers[Global.current_layer][0]
|
||||
if image.is_invisible():
|
||||
return
|
||||
var new_image := Image.new()
|
||||
new_image.copy_from(image)
|
||||
new_image.lock()
|
||||
|
||||
Global.canvas.handle_undo("Draw")
|
||||
for xx in image.get_size().x:
|
||||
for yy in image.get_size().y:
|
||||
var pos = Vector2(xx, yy)
|
||||
var current_pixel := image.get_pixelv(pos)
|
||||
if current_pixel.a == 0:
|
||||
continue
|
||||
|
||||
for i in range(1, thickness + 1):
|
||||
if inside_image:
|
||||
var outline_pos : Vector2 = pos + Vector2.LEFT # Left
|
||||
if outline_pos.x < 0 || image.get_pixelv(outline_pos).a == 0:
|
||||
var new_pos : Vector2 = pos + Vector2.RIGHT * (i - 1)
|
||||
if new_pos.x < Global.canvas.size.x:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a > 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
outline_pos = pos + Vector2.RIGHT # Right
|
||||
if outline_pos.x >= Global.canvas.size.x || image.get_pixelv(outline_pos).a == 0:
|
||||
var new_pos : Vector2 = pos + Vector2.LEFT * (i - 1)
|
||||
if new_pos.x >= 0:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a > 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
outline_pos = pos + Vector2.UP # Up
|
||||
if outline_pos.y < 0 || image.get_pixelv(outline_pos).a == 0:
|
||||
var new_pos : Vector2 = pos + Vector2.DOWN * (i - 1)
|
||||
if new_pos.y < Global.canvas.size.y:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a > 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
outline_pos = pos + Vector2.DOWN # Down
|
||||
if outline_pos.y >= Global.canvas.size.y || image.get_pixelv(outline_pos).a == 0:
|
||||
var new_pos : Vector2 = pos + Vector2.UP * (i - 1)
|
||||
if new_pos.y >= 0:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a > 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
if diagonal:
|
||||
outline_pos = pos + (Vector2.LEFT + Vector2.UP) # Top left
|
||||
if (outline_pos.x < 0 && outline_pos.y < 0) || image.get_pixelv(outline_pos).a == 0:
|
||||
var new_pos : Vector2 = pos + (Vector2.RIGHT + Vector2.DOWN) * (i - 1)
|
||||
if new_pos.x < Global.canvas.size.x && new_pos.y < Global.canvas.size.y:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a > 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
outline_pos = pos + (Vector2.LEFT + Vector2.DOWN) # Bottom left
|
||||
if (outline_pos.x < 0 && outline_pos.y >= Global.canvas.size.y) || image.get_pixelv(outline_pos).a == 0:
|
||||
var new_pos : Vector2 = pos + (Vector2.RIGHT + Vector2.UP) * (i - 1)
|
||||
if new_pos.x < Global.canvas.size.x && new_pos.y >= 0:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a > 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
outline_pos = pos + (Vector2.RIGHT + Vector2.UP) # Top right
|
||||
if (outline_pos.x >= Global.canvas.size.x && outline_pos.y < 0) || image.get_pixelv(outline_pos).a == 0:
|
||||
var new_pos : Vector2 = pos + (Vector2.LEFT + Vector2.DOWN) * (i - 1)
|
||||
if new_pos.x >= 0 && new_pos.y < Global.canvas.size.y:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a > 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
outline_pos = pos + (Vector2.RIGHT + Vector2.DOWN) # Bottom right
|
||||
if (outline_pos.x >= Global.canvas.size.x && outline_pos.y >= Global.canvas.size.y) || image.get_pixelv(outline_pos).a == 0:
|
||||
var new_pos : Vector2 = pos + (Vector2.LEFT + Vector2.UP) * (i - 1)
|
||||
if new_pos.x >= 0 && new_pos.y >= 0:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a > 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
else:
|
||||
var new_pos : Vector2 = pos + Vector2.LEFT * i # Left
|
||||
if new_pos.x >= 0:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a == 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
new_pos = pos + Vector2.RIGHT * i # Right
|
||||
if new_pos.x < Global.canvas.size.x:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a == 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
new_pos = pos + Vector2.UP * i # Up
|
||||
if new_pos.y >= 0:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a == 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
new_pos = pos + Vector2.DOWN * i # Down
|
||||
if new_pos.y < Global.canvas.size.y:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a == 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
if diagonal:
|
||||
new_pos = pos + (Vector2.LEFT + Vector2.UP) * i # Top left
|
||||
if new_pos.x >= 0 && new_pos.y >= 0:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a == 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
new_pos = pos + (Vector2.LEFT + Vector2.DOWN) * i # Bottom left
|
||||
if new_pos.x >= 0 && new_pos.y < Global.canvas.size.y:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a == 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
new_pos = pos + (Vector2.RIGHT + Vector2.UP) * i # Top right
|
||||
if new_pos.x < Global.canvas.size.x && new_pos.y >= 0:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a == 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
new_pos = pos + (Vector2.RIGHT + Vector2.DOWN) * i # Bottom right
|
||||
if new_pos.x < Global.canvas.size.x && new_pos.y < Global.canvas.size.y:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a == 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
image.copy_from(new_image)
|
||||
Global.canvas.handle_redo("Draw")
|
69
src/Dialogs/OutlineDialog.tscn
Normal file
69
src/Dialogs/OutlineDialog.tscn
Normal file
|
@ -0,0 +1,69 @@
|
|||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://src/Dialogs/OutlineDialog.gd" type="Script" id=1]
|
||||
|
||||
|
||||
[node name="OutlineDialog" type="ConfirmationDialog"]
|
||||
visible = true
|
||||
margin_right = 200.0
|
||||
margin_bottom = 70.0
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="OptionsContainer" type="GridContainer" parent="."]
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
margin_left = -121.0
|
||||
margin_top = -52.0
|
||||
margin_right = 121.0
|
||||
margin_bottom = 24.0
|
||||
custom_constants/vseparation = 4
|
||||
custom_constants/hseparation = 4
|
||||
columns = 2
|
||||
|
||||
[node name="ThickLabel" type="Label" parent="OptionsContainer"]
|
||||
margin_top = 5.0
|
||||
margin_right = 90.0
|
||||
margin_bottom = 19.0
|
||||
text = "Thickness:"
|
||||
|
||||
[node name="ThickValue" type="SpinBox" parent="OptionsContainer"]
|
||||
margin_left = 94.0
|
||||
margin_right = 242.0
|
||||
margin_bottom = 24.0
|
||||
mouse_default_cursor_shape = 2
|
||||
min_value = 1.0
|
||||
max_value = 16384.0
|
||||
value = 1.0
|
||||
suffix = "px"
|
||||
|
||||
[node name="OutlineColorLabel" type="Label" parent="OptionsContainer"]
|
||||
margin_top = 31.0
|
||||
margin_right = 90.0
|
||||
margin_bottom = 45.0
|
||||
text = "Fill with color:"
|
||||
|
||||
[node name="OutlineColor" type="ColorPickerButton" parent="OptionsContainer"]
|
||||
margin_left = 94.0
|
||||
margin_top = 28.0
|
||||
margin_right = 242.0
|
||||
margin_bottom = 48.0
|
||||
rect_min_size = Vector2( 64, 20 )
|
||||
color = Color( 1, 0, 0, 1 )
|
||||
|
||||
[node name="DiagonalCheckBox" type="CheckBox" parent="OptionsContainer"]
|
||||
margin_top = 52.0
|
||||
margin_right = 90.0
|
||||
margin_bottom = 76.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Diagonal"
|
||||
|
||||
[node name="InsideImageCheckBox" type="CheckBox" parent="OptionsContainer"]
|
||||
margin_left = 94.0
|
||||
margin_top = 52.0
|
||||
margin_right = 242.0
|
||||
margin_bottom = 76.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Place inside image"
|
||||
[connection signal="confirmed" from="." to="." method="_on_OutlineDialog_confirmed"]
|
555
src/Dialogs/PreferencesDialog.gd
Normal file
555
src/Dialogs/PreferencesDialog.gd
Normal file
|
@ -0,0 +1,555 @@
|
|||
extends AcceptDialog
|
||||
|
||||
onready var tree : Tree = $HSplitContainer/Tree
|
||||
onready var right_side : VBoxContainer = $HSplitContainer/ScrollContainer/VBoxContainer
|
||||
onready var general = $HSplitContainer/ScrollContainer/VBoxContainer/General
|
||||
onready var languages = $HSplitContainer/ScrollContainer/VBoxContainer/Languages
|
||||
onready var themes = $HSplitContainer/ScrollContainer/VBoxContainer/Themes
|
||||
onready var canvas = $HSplitContainer/ScrollContainer/VBoxContainer/Canvas
|
||||
onready var image = $HSplitContainer/ScrollContainer/VBoxContainer/Image
|
||||
onready var shortcuts = $HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts
|
||||
|
||||
onready var open_last_project_button = $HSplitContainer/ScrollContainer/VBoxContainer/General/OpenLastProject
|
||||
onready var smooth_zoom_button = $HSplitContainer/ScrollContainer/VBoxContainer/General/SmoothZoom
|
||||
onready var sensitivity_option = $HSplitContainer/ScrollContainer/VBoxContainer/General/PressureSentivity/PressureSensitivityOptionButton
|
||||
onready var left_tool_icon = $HSplitContainer/ScrollContainer/VBoxContainer/General/GridContainer/LeftToolIconCheckbox
|
||||
onready var right_tool_icon = $HSplitContainer/ScrollContainer/VBoxContainer/General/GridContainer/RightToolIconCheckbox
|
||||
|
||||
onready var default_width_value = $HSplitContainer/ScrollContainer/VBoxContainer/Image/ImageOptions/ImageDefaultWidth
|
||||
onready var default_height_value = $HSplitContainer/ScrollContainer/VBoxContainer/Image/ImageOptions/ImageDefaultHeight
|
||||
onready var default_fill_color = $HSplitContainer/ScrollContainer/VBoxContainer/Image/ImageOptions/DefaultFillColor
|
||||
|
||||
onready var grid_width_value = $HSplitContainer/ScrollContainer/VBoxContainer/Canvas/GridOptions/GridWidthValue
|
||||
onready var grid_height_value = $HSplitContainer/ScrollContainer/VBoxContainer/Canvas/GridOptions/GridHeightValue
|
||||
onready var grid_color = $HSplitContainer/ScrollContainer/VBoxContainer/Canvas/GridOptions/GridColor
|
||||
onready var guide_color = $HSplitContainer/ScrollContainer/VBoxContainer/Canvas/GuideOptions/GuideColor
|
||||
|
||||
onready var checker_size_value = $HSplitContainer/ScrollContainer/VBoxContainer/Canvas/CheckerOptions/CheckerSizeValue
|
||||
onready var checker_color_1 = $HSplitContainer/ScrollContainer/VBoxContainer/Canvas/CheckerOptions/CheckerColor1
|
||||
onready var checker_color_2 = $HSplitContainer/ScrollContainer/VBoxContainer/Canvas/CheckerOptions/CheckerColor2
|
||||
|
||||
# Shortcuts
|
||||
onready var theme_font_color : Color = $Popups/ShortcutSelector/EnteredShortcut.get_color("font_color")
|
||||
var default_shortcuts_preset := {}
|
||||
var custom_shortcuts_preset := {}
|
||||
var action_being_edited := ""
|
||||
var shortcut_already_assigned = false
|
||||
var old_input_event : InputEventKey
|
||||
var new_input_event : InputEventKey
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
# Disable input until the shortcut selector is displayed
|
||||
set_process_input(false)
|
||||
|
||||
# Replace OK with Close since preference changes are being applied immediately, not after OK confirmation
|
||||
get_ok().text = tr("Close")
|
||||
|
||||
for child in languages.get_children():
|
||||
if child is Button:
|
||||
child.connect("pressed", self, "_on_Language_pressed", [child])
|
||||
child.hint_tooltip = child.name
|
||||
|
||||
for child in themes.get_children():
|
||||
if child is Button:
|
||||
child.connect("pressed", self, "_on_Theme_pressed", [child])
|
||||
|
||||
if Global.config_cache.has_section_key("preferences", "theme"):
|
||||
var theme_id = Global.config_cache.get_value("preferences", "theme")
|
||||
change_theme(theme_id)
|
||||
themes.get_child(theme_id).pressed = true
|
||||
else:
|
||||
change_theme(0)
|
||||
themes.get_child(0).pressed = true
|
||||
|
||||
# Set default values for General options
|
||||
if Global.config_cache.has_section_key("preferences", "open_last_project"):
|
||||
Global.open_last_project = Global.config_cache.get_value("preferences", "open_last_project")
|
||||
open_last_project_button.pressed = Global.open_last_project
|
||||
if Global.config_cache.has_section_key("preferences", "smooth_zoom"):
|
||||
Global.smooth_zoom = Global.config_cache.get_value("preferences", "smooth_zoom")
|
||||
smooth_zoom_button.pressed = Global.smooth_zoom
|
||||
if Global.config_cache.has_section_key("preferences", "pressure_sensitivity"):
|
||||
Global.pressure_sensitivity_mode = Global.config_cache.get_value("preferences", "pressure_sensitivity")
|
||||
sensitivity_option.selected = Global.pressure_sensitivity_mode
|
||||
|
||||
if Global.config_cache.has_section_key("preferences", "show_left_tool_icon"):
|
||||
Global.show_left_tool_icon = Global.config_cache.get_value("preferences", "show_left_tool_icon")
|
||||
left_tool_icon.pressed = Global.show_left_tool_icon
|
||||
if Global.config_cache.has_section_key("preferences", "show_right_tool_icon"):
|
||||
Global.show_right_tool_icon = Global.config_cache.get_value("preferences", "show_right_tool_icon")
|
||||
right_tool_icon.pressed = Global.show_right_tool_icon
|
||||
|
||||
# Get autosave settings
|
||||
if Global.config_cache.has_section_key("preferences", "autosave_interval"):
|
||||
var autosave_interval = Global.config_cache.get_value("preferences", "autosave_interval")
|
||||
OpenSave.set_autosave_interval(autosave_interval)
|
||||
general.get_node("AutosaveInterval/AutosaveInterval").value = autosave_interval
|
||||
if Global.config_cache.has_section_key("preferences", "enable_autosave"):
|
||||
var enable_autosave = Global.config_cache.get_value("preferences", "enable_autosave")
|
||||
OpenSave.toggle_autosave(enable_autosave)
|
||||
general.get_node("EnableAutosave").pressed = enable_autosave
|
||||
|
||||
# Set default values for Canvas options
|
||||
if Global.config_cache.has_section_key("preferences", "grid_size"):
|
||||
var grid_size = Global.config_cache.get_value("preferences", "grid_size")
|
||||
Global.grid_width = int(grid_size.x)
|
||||
Global.grid_height = int(grid_size.y)
|
||||
grid_width_value.value = grid_size.x
|
||||
grid_height_value.value = grid_size.y
|
||||
|
||||
if Global.config_cache.has_section_key("preferences", "grid_color"):
|
||||
Global.grid_color = Global.config_cache.get_value("preferences", "grid_color")
|
||||
grid_color.color = Global.grid_color
|
||||
|
||||
if Global.config_cache.has_section_key("preferences", "checker_size"):
|
||||
var checker_size = Global.config_cache.get_value("preferences", "checker_size")
|
||||
Global.checker_size = int(checker_size)
|
||||
checker_size_value.value = checker_size
|
||||
|
||||
if Global.config_cache.has_section_key("preferences", "checker_color_1"):
|
||||
Global.checker_color_1 = Global.config_cache.get_value("preferences", "checker_color_1")
|
||||
checker_color_1.color = Global.checker_color_1
|
||||
|
||||
if Global.config_cache.has_section_key("preferences", "checker_color_2"):
|
||||
Global.checker_color_2 = Global.config_cache.get_value("preferences", "checker_color_2")
|
||||
checker_color_2.color = Global.checker_color_2
|
||||
|
||||
Global.transparent_checker._ready()
|
||||
|
||||
if Global.config_cache.has_section_key("preferences", "guide_color"):
|
||||
Global.guide_color = Global.config_cache.get_value("preferences", "guide_color")
|
||||
for canvas in Global.canvases:
|
||||
for guide in canvas.get_children():
|
||||
if guide is Guide:
|
||||
guide.default_color = Global.guide_color
|
||||
guide_color.color = Global.guide_color
|
||||
|
||||
# Set default values for Image
|
||||
if Global.config_cache.has_section_key("preferences", "default_width"):
|
||||
var default_width = Global.config_cache.get_value("preferences", "default_width")
|
||||
Global.default_image_width = int(default_width)
|
||||
default_width_value.value = Global.default_image_width
|
||||
|
||||
if Global.config_cache.has_section_key("preferences", "default_height"):
|
||||
var default_height = Global.config_cache.get_value("preferences", "default_height")
|
||||
Global.default_image_height = int(default_height)
|
||||
default_height_value.value = Global.default_image_height
|
||||
|
||||
if Global.config_cache.has_section_key("preferences", "default_fill_color"):
|
||||
var fill_color = Global.config_cache.get_value("preferences", "default_fill_color")
|
||||
Global.default_fill_color = fill_color
|
||||
default_fill_color.color = Global.default_fill_color
|
||||
|
||||
guide_color.get_picker().presets_visible = false
|
||||
grid_color.get_picker().presets_visible = false
|
||||
checker_color_1.get_picker().presets_visible = false
|
||||
checker_color_2.get_picker().presets_visible = false
|
||||
default_fill_color.get_picker().presets_visible = false
|
||||
|
||||
# Get default preset for shortcuts from project input map
|
||||
# Buttons in shortcuts selector should be called the same as actions
|
||||
for shortcut_grid_item in shortcuts.get_node("Shortcuts").get_children():
|
||||
if shortcut_grid_item is Button:
|
||||
var input_events = InputMap.get_action_list(shortcut_grid_item.name)
|
||||
if input_events.size() > 1:
|
||||
printerr("Every shortcut action should have just one input event assigned in input map")
|
||||
shortcut_grid_item.text = (input_events[0] as InputEventKey).as_text()
|
||||
shortcut_grid_item.connect("pressed", self, "_on_Shortcut_button_pressed", [shortcut_grid_item])
|
||||
default_shortcuts_preset[shortcut_grid_item.name] = input_events[0]
|
||||
|
||||
# Load custom shortcuts from the config file
|
||||
custom_shortcuts_preset = default_shortcuts_preset.duplicate()
|
||||
for action in default_shortcuts_preset:
|
||||
var saved_input_event = Global.config_cache.get_value("shortcuts", action, 0)
|
||||
if saved_input_event is InputEventKey:
|
||||
custom_shortcuts_preset[action] = saved_input_event
|
||||
|
||||
var shortcuts_preset = Global.config_cache.get_value("shortcuts", "shortcuts_preset", 0)
|
||||
shortcuts.get_node("HBoxContainer/PresetOptionButton").select(shortcuts_preset)
|
||||
_on_PresetOptionButton_item_selected(shortcuts_preset)
|
||||
|
||||
|
||||
func _input(event : InputEvent) -> void:
|
||||
if event is InputEventKey:
|
||||
if event.pressed:
|
||||
if event.scancode == KEY_ESCAPE:
|
||||
$Popups/ShortcutSelector.hide()
|
||||
else:
|
||||
# Check if shortcut was already used
|
||||
for action in InputMap.get_actions():
|
||||
for input_event in InputMap.get_action_list(action):
|
||||
if input_event is InputEventKey:
|
||||
if OS.get_scancode_string(input_event.get_scancode_with_modifiers()) == OS.get_scancode_string(event.get_scancode_with_modifiers()):
|
||||
$Popups/ShortcutSelector/EnteredShortcut.text = tr("Already assigned")
|
||||
$Popups/ShortcutSelector/EnteredShortcut.add_color_override("font_color", Color.crimson)
|
||||
get_tree().set_input_as_handled()
|
||||
shortcut_already_assigned = true
|
||||
return
|
||||
|
||||
# Store new shortcut
|
||||
shortcut_already_assigned = false
|
||||
old_input_event = InputMap.get_action_list(action_being_edited)[0]
|
||||
new_input_event = event
|
||||
$Popups/ShortcutSelector/EnteredShortcut.text = OS.get_scancode_string(event.get_scancode_with_modifiers())
|
||||
$Popups/ShortcutSelector/EnteredShortcut.add_color_override("font_color", theme_font_color)
|
||||
get_tree().set_input_as_handled()
|
||||
|
||||
|
||||
func _on_PreferencesDialog_about_to_show(changed_language := false) -> void:
|
||||
var root := tree.create_item()
|
||||
var general_button := tree.create_item(root)
|
||||
var language_button := tree.create_item(root)
|
||||
var theme_button := tree.create_item(root)
|
||||
var canvas_button := tree.create_item(root)
|
||||
var image_button := tree.create_item(root)
|
||||
var shortcuts_button := tree.create_item(root)
|
||||
|
||||
general_button.set_text(0, " " + tr("General"))
|
||||
# We use metadata to avoid being affected by translations
|
||||
general_button.set_metadata(0, "General")
|
||||
language_button.set_text(0, " " + tr("Language"))
|
||||
language_button.set_metadata(0, "Language")
|
||||
theme_button.set_text(0, " " + tr("Themes"))
|
||||
theme_button.set_metadata(0, "Themes")
|
||||
canvas_button.set_text(0, " " + tr("Canvas"))
|
||||
canvas_button.set_metadata(0, "Canvas")
|
||||
image_button.set_text(0, " " + tr("Image"))
|
||||
image_button.set_metadata(0, "Image")
|
||||
shortcuts_button.set_text(0, " " + tr("Shortcuts"))
|
||||
shortcuts_button.set_metadata(0, "Shortcuts")
|
||||
|
||||
if changed_language:
|
||||
language_button.select(0)
|
||||
else:
|
||||
general_button.select(0)
|
||||
|
||||
|
||||
func _on_PreferencesDialog_popup_hide() -> void:
|
||||
tree.clear()
|
||||
|
||||
|
||||
func _on_Tree_item_selected() -> void:
|
||||
for child in right_side.get_children():
|
||||
child.visible = false
|
||||
var selected : String = tree.get_selected().get_metadata(0)
|
||||
if "General" in selected:
|
||||
general.visible = true
|
||||
elif "Language" in selected:
|
||||
languages.visible = true
|
||||
elif "Themes" in selected:
|
||||
themes.visible = true
|
||||
elif "Canvas" in selected:
|
||||
canvas.visible = true
|
||||
elif "Image" in selected:
|
||||
image.visible = true
|
||||
elif "Shortcuts" in selected:
|
||||
shortcuts.visible = true
|
||||
|
||||
|
||||
func _on_PressureSensitivityOptionButton_item_selected(id : int) -> void:
|
||||
Global.pressure_sensitivity_mode = id
|
||||
Global.config_cache.set_value("preferences", "pressure_sensitivity", id)
|
||||
Global.config_cache.save("user://cache.ini")
|
||||
|
||||
|
||||
func _on_SmoothZoom_pressed() -> void:
|
||||
Global.smooth_zoom = !Global.smooth_zoom
|
||||
Global.config_cache.set_value("preferences", "smooth_zoom", Global.smooth_zoom)
|
||||
Global.config_cache.save("user://cache.ini")
|
||||
|
||||
|
||||
func _on_Language_pressed(button : Button) -> void:
|
||||
var index := 0
|
||||
var i := -1
|
||||
for child in languages.get_children():
|
||||
if child is Button:
|
||||
if child == button:
|
||||
button.pressed = true
|
||||
index = i
|
||||
else:
|
||||
child.pressed = false
|
||||
i += 1
|
||||
if index == -1:
|
||||
TranslationServer.set_locale(OS.get_locale())
|
||||
else:
|
||||
TranslationServer.set_locale(Global.loaded_locales[index])
|
||||
|
||||
if "zh" in TranslationServer.get_locale():
|
||||
Global.control.theme.default_font = preload("res://Assets/Fonts/CJK/NotoSansCJKtc-Regular.tres")
|
||||
else:
|
||||
Global.control.theme.default_font = preload("res://Assets/Fonts/Roboto-Regular.tres")
|
||||
|
||||
Global.config_cache.set_value("preferences", "locale", TranslationServer.get_locale())
|
||||
Global.config_cache.save("user://cache.ini")
|
||||
|
||||
# Update Translations
|
||||
Global.update_hint_tooltips()
|
||||
_on_PreferencesDialog_popup_hide()
|
||||
_on_PreferencesDialog_about_to_show(true)
|
||||
|
||||
|
||||
func _on_Theme_pressed(button : Button) -> void:
|
||||
var index := 0
|
||||
var i := 0
|
||||
for child in themes.get_children():
|
||||
if child is Button:
|
||||
if child == button:
|
||||
button.pressed = true
|
||||
index = i
|
||||
else:
|
||||
child.pressed = false
|
||||
i += 1
|
||||
|
||||
change_theme(index)
|
||||
|
||||
Global.config_cache.set_value("preferences", "theme", index)
|
||||
Global.config_cache.save("user://cache.ini")
|
||||
|
||||
|
||||
func change_theme(ID : int) -> void:
|
||||
var font = Global.control.theme.default_font
|
||||
var main_theme
|
||||
var top_menu_style
|
||||
var ruler_style
|
||||
if ID == 0: # Dark Theme
|
||||
Global.theme_type = "Dark"
|
||||
VisualServer.set_default_clear_color(Color(0.247059, 0.25098, 0.247059))
|
||||
main_theme = preload("res://Themes & Styles/Dark Theme/Dark Theme.tres")
|
||||
top_menu_style = preload("res://Themes & Styles/Dark Theme/DarkTopMenuStyle.tres")
|
||||
ruler_style = preload("res://Themes & Styles/Dark Theme/DarkRulerStyle.tres")
|
||||
elif ID == 1: # Gray Theme
|
||||
Global.theme_type = "Dark"
|
||||
VisualServer.set_default_clear_color(Color(0.301961, 0.301961, 0.301961))
|
||||
main_theme = preload("res://Themes & Styles/Gray Theme/Gray Theme.tres")
|
||||
top_menu_style = preload("res://Themes & Styles/Gray Theme/GrayTopMenuStyle.tres")
|
||||
ruler_style = preload("res://Themes & Styles/Dark Theme/DarkRulerStyle.tres")
|
||||
elif ID == 2: # Godot's Theme
|
||||
Global.theme_type = "Dark"
|
||||
VisualServer.set_default_clear_color(Color(0.27451, 0.278431, 0.305882))
|
||||
main_theme = preload("res://Themes & Styles/Godot\'s Theme/Godot\'s Theme.tres")
|
||||
top_menu_style = preload("res://Themes & Styles/Godot\'s Theme/TopMenuStyle.tres")
|
||||
ruler_style = preload("res://Themes & Styles/Godot\'s Theme/RulerStyle.tres")
|
||||
elif ID == 3: # Gold Theme
|
||||
Global.theme_type = "Gold"
|
||||
VisualServer.set_default_clear_color(Color(0.694118, 0.619608, 0.458824))
|
||||
main_theme = preload("res://Themes & Styles/Gold Theme/Gold Theme.tres")
|
||||
top_menu_style = preload("res://Themes & Styles/Gold Theme/GoldTopMenuStyle.tres")
|
||||
ruler_style = preload("res://Themes & Styles/Gold Theme/GoldRulerStyle.tres")
|
||||
elif ID == 4: # Light Theme
|
||||
Global.theme_type = "Light"
|
||||
VisualServer.set_default_clear_color(Color(0.705882, 0.705882, 0.705882))
|
||||
main_theme = preload("res://Themes & Styles/Light Theme/Light Theme.tres")
|
||||
top_menu_style = preload("res://Themes & Styles/Light Theme/LightTopMenuStyle.tres")
|
||||
ruler_style = preload("res://Themes & Styles/Light Theme/LightRulerStyle.tres")
|
||||
|
||||
Global.control.theme = main_theme
|
||||
Global.control.theme.default_font = font
|
||||
Global.top_menu_container.add_stylebox_override("panel", top_menu_style)
|
||||
Global.horizontal_ruler.add_stylebox_override("normal", ruler_style)
|
||||
Global.horizontal_ruler.add_stylebox_override("pressed", ruler_style)
|
||||
Global.horizontal_ruler.add_stylebox_override("hover", ruler_style)
|
||||
Global.horizontal_ruler.add_stylebox_override("focus", ruler_style)
|
||||
Global.vertical_ruler.add_stylebox_override("normal", ruler_style)
|
||||
Global.vertical_ruler.add_stylebox_override("pressed", ruler_style)
|
||||
Global.vertical_ruler.add_stylebox_override("hover", ruler_style)
|
||||
Global.vertical_ruler.add_stylebox_override("focus", ruler_style)
|
||||
|
||||
for button in get_tree().get_nodes_in_group("UIButtons"):
|
||||
if button is TextureButton:
|
||||
var last_backslash = button.texture_normal.resource_path.get_base_dir().find_last("/")
|
||||
var button_category = button.texture_normal.resource_path.get_base_dir().right(last_backslash + 1)
|
||||
var normal_file_name = button.texture_normal.resource_path.get_file()
|
||||
button.texture_normal = load("res://Assets/Graphics/%s Themes/%s/%s" % [Global.theme_type, button_category, normal_file_name])
|
||||
if button.texture_pressed:
|
||||
var pressed_file_name = button.texture_pressed.resource_path.get_file()
|
||||
button.texture_pressed = load("res://Assets/Graphics/%s Themes/%s/%s" % [Global.theme_type, button_category, pressed_file_name])
|
||||
if button.texture_hover:
|
||||
var hover_file_name = button.texture_hover.resource_path.get_file()
|
||||
button.texture_hover = load("res://Assets/Graphics/%s Themes/%s/%s" % [Global.theme_type, button_category, hover_file_name])
|
||||
if button.texture_disabled:
|
||||
var disabled_file_name = button.texture_disabled.resource_path.get_file()
|
||||
button.texture_disabled = load("res://Assets/Graphics/%s Themes/%s/%s" % [Global.theme_type, button_category, disabled_file_name])
|
||||
elif button is Button:
|
||||
var theme_type := Global.theme_type
|
||||
if theme_type == "Gold":
|
||||
theme_type = "Light"
|
||||
var texture : TextureRect = button.get_child(0)
|
||||
var last_backslash = texture.texture.resource_path.get_base_dir().find_last("/")
|
||||
var button_category = texture.texture.resource_path.get_base_dir().right(last_backslash + 1)
|
||||
var normal_file_name = texture.texture.resource_path.get_file()
|
||||
texture.texture = load("res://Assets/Graphics/%s Themes/%s/%s" % [theme_type, button_category, normal_file_name])
|
||||
|
||||
# Make sure the frame text gets updated
|
||||
Global.current_frame = Global.current_frame
|
||||
|
||||
$Popups/ShortcutSelector.theme = main_theme
|
||||
|
||||
|
||||
func apply_shortcuts_preset(preset) -> void:
|
||||
for action in preset:
|
||||
var old_input_event : InputEventKey = InputMap.get_action_list(action)[0]
|
||||
set_action_shortcut(action, old_input_event, preset[action])
|
||||
shortcuts.get_node("Shortcuts/" + action).text = OS.get_scancode_string(preset[action].get_scancode_with_modifiers())
|
||||
|
||||
|
||||
func toggle_shortcut_buttons(enabled : bool) -> void:
|
||||
for shortcut_grid_item in shortcuts.get_node("Shortcuts").get_children():
|
||||
if shortcut_grid_item is Button:
|
||||
shortcut_grid_item.disabled = not enabled
|
||||
if shortcut_grid_item.disabled:
|
||||
shortcut_grid_item.mouse_default_cursor_shape = Control.CURSOR_FORBIDDEN
|
||||
else:
|
||||
shortcut_grid_item.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND
|
||||
|
||||
|
||||
func set_action_shortcut(action : String, old_input : InputEventKey, new_input : InputEventKey) -> void:
|
||||
InputMap.action_erase_event(action, old_input)
|
||||
InputMap.action_add_event(action, new_input)
|
||||
Global.update_hint_tooltips()
|
||||
# Set shortcut to switch colors button
|
||||
if action == "switch_colors":
|
||||
Global.color_switch_button.shortcut.shortcut = InputMap.get_action_list("switch_colors")[0]
|
||||
|
||||
|
||||
func _on_GridWidthValue_value_changed(value : float) -> void:
|
||||
Global.grid_width = value
|
||||
Global.canvas.update()
|
||||
Global.config_cache.set_value("preferences", "grid_size", Vector2(value, grid_height_value.value))
|
||||
Global.config_cache.save("user://cache.ini")
|
||||
|
||||
|
||||
func _on_GridHeightValue_value_changed(value : float) -> void:
|
||||
Global.grid_height = value
|
||||
Global.canvas.update()
|
||||
Global.config_cache.set_value("preferences", "grid_size", Vector2(grid_width_value.value, value))
|
||||
Global.config_cache.save("user://cache.ini")
|
||||
|
||||
|
||||
func _on_GridColor_color_changed(color : Color) -> void:
|
||||
Global.grid_color = color
|
||||
Global.canvas.update()
|
||||
Global.config_cache.set_value("preferences", "grid_color", color)
|
||||
Global.config_cache.save("user://cache.ini")
|
||||
|
||||
|
||||
func _on_CheckerSize_value_changed(value : float) -> void:
|
||||
Global.checker_size = value
|
||||
Global.transparent_checker._ready()
|
||||
Global.config_cache.set_value("preferences", "checker_size", value)
|
||||
Global.config_cache.save("user://cache.ini")
|
||||
|
||||
|
||||
func _on_CheckerColor1_color_changed(color : Color) -> void:
|
||||
Global.checker_color_1 = color
|
||||
Global.transparent_checker._ready()
|
||||
Global.config_cache.set_value("preferences", "checker_color_1", color)
|
||||
Global.config_cache.save("user://cache.ini")
|
||||
|
||||
|
||||
func _on_CheckerColor2_color_changed(color : Color) -> void:
|
||||
Global.checker_color_2 = color
|
||||
Global.transparent_checker._ready()
|
||||
Global.config_cache.set_value("preferences", "checker_color_2", color)
|
||||
Global.config_cache.save("user://cache.ini")
|
||||
|
||||
|
||||
func _on_GuideColor_color_changed(color : Color) -> void:
|
||||
Global.guide_color = color
|
||||
for canvas in Global.canvases:
|
||||
for guide in canvas.get_children():
|
||||
if guide is Guide:
|
||||
guide.default_color = color
|
||||
Global.config_cache.set_value("preferences", "guide_color", color)
|
||||
Global.config_cache.save("user://cache.ini")
|
||||
|
||||
|
||||
func _on_ImageDefaultWidth_value_changed(value: float) -> void:
|
||||
Global.default_image_width = value
|
||||
Global.config_cache.set_value("preferences", "default_width", value)
|
||||
Global.config_cache.save("user://cache.ini")
|
||||
|
||||
|
||||
func _on_ImageDefaultHeight_value_changed(value: float) -> void:
|
||||
Global.default_image_height = value
|
||||
Global.config_cache.set_value("preferences", "default_height", value)
|
||||
Global.config_cache.save("user://cache.ini")
|
||||
|
||||
|
||||
func _on_DefaultBackground_color_changed(color: Color) -> void:
|
||||
Global.default_fill_color = color
|
||||
Global.config_cache.set_value("preferences", "default_fill_color", color)
|
||||
Global.config_cache.save("user://cache.ini")
|
||||
|
||||
|
||||
func _on_LeftIndicatorCheckbox_toggled(button_pressed : bool) -> void:
|
||||
Global.left_square_indicator_visible = button_pressed
|
||||
|
||||
|
||||
func _on_RightIndicatorCheckbox_toggled(button_pressed : bool) -> void:
|
||||
Global.right_square_indicator_visible = button_pressed
|
||||
|
||||
|
||||
func _on_LeftToolIconCheckbox_toggled(button_pressed : bool) -> void:
|
||||
Global.show_left_tool_icon = button_pressed
|
||||
Global.config_cache.set_value("preferences", "show_left_tool_icon", Global.show_left_tool_icon)
|
||||
Global.config_cache.save("user://cache.ini")
|
||||
|
||||
|
||||
func _on_RightToolIconCheckbox_toggled(button_pressed : bool) -> void:
|
||||
Global.show_right_tool_icon = button_pressed
|
||||
Global.config_cache.set_value("preferences", "show_right_tool_icon", Global.show_right_tool_icon)
|
||||
Global.config_cache.save("user://cache.ini")
|
||||
|
||||
|
||||
func _on_Shortcut_button_pressed(button : Button) -> void:
|
||||
set_process_input(true)
|
||||
action_being_edited = button.name
|
||||
new_input_event = InputMap.get_action_list(button.name)[0]
|
||||
shortcut_already_assigned = true
|
||||
$Popups/ShortcutSelector.popup_centered()
|
||||
|
||||
|
||||
func _on_ShortcutSelector_popup_hide() -> void:
|
||||
set_process_input(false)
|
||||
$Popups/ShortcutSelector/EnteredShortcut.text = ""
|
||||
|
||||
|
||||
func _on_PresetOptionButton_item_selected(id : int) -> void:
|
||||
# Only custom preset which is modifiable
|
||||
toggle_shortcut_buttons(true if id == 1 else false)
|
||||
match id:
|
||||
0:
|
||||
apply_shortcuts_preset(default_shortcuts_preset)
|
||||
1:
|
||||
apply_shortcuts_preset(custom_shortcuts_preset)
|
||||
Global.config_cache.set_value("shortcuts", "shortcuts_preset", id)
|
||||
Global.config_cache.save("user://cache.ini")
|
||||
|
||||
|
||||
func _on_ShortcutSelector_confirmed() -> void:
|
||||
if not shortcut_already_assigned:
|
||||
set_action_shortcut(action_being_edited, old_input_event, new_input_event)
|
||||
custom_shortcuts_preset[action_being_edited] = new_input_event
|
||||
Global.config_cache.set_value("shortcuts", action_being_edited, new_input_event)
|
||||
Global.config_cache.save("user://cache.ini")
|
||||
shortcuts.get_node("Shortcuts/" + action_being_edited).text = OS.get_scancode_string(new_input_event.get_scancode_with_modifiers())
|
||||
$Popups/ShortcutSelector.hide()
|
||||
|
||||
|
||||
func _on_OpenLastProject_pressed() -> void:
|
||||
Global.open_last_project = !Global.open_last_project
|
||||
Global.config_cache.set_value("preferences", "open_last_project", Global.open_last_project)
|
||||
Global.config_cache.save("user://cache.ini")
|
||||
|
||||
|
||||
func _on_EnableAutosave_toggled(button_pressed : bool) -> void:
|
||||
OpenSave.toggle_autosave(button_pressed)
|
||||
Global.config_cache.set_value("preferences", "enable_autosave", button_pressed)
|
||||
Global.config_cache.save("user://cache.ini")
|
||||
|
||||
|
||||
func _on_AutosaveInterval_value_changed(value : float) -> void:
|
||||
OpenSave.set_autosave_interval(value)
|
||||
Global.config_cache.set_value("preferences", "autosave_interval", value)
|
||||
Global.config_cache.save("user://cache.ini")
|
874
src/Dialogs/PreferencesDialog.tscn
Normal file
874
src/Dialogs/PreferencesDialog.tscn
Normal file
|
@ -0,0 +1,874 @@
|
|||
[gd_scene load_steps=4 format=2]
|
||||
|
||||
[ext_resource path="res://src/Dialogs/PreferencesDialog.gd" type="Script" id=1]
|
||||
[ext_resource path="res://Assets/Fonts/Roboto-Regular.tres" type="DynamicFont" id=2]
|
||||
[ext_resource path="res://Assets/Fonts/CJK/NotoSansCJKtc-Regular.tres" type="DynamicFont" id=3]
|
||||
|
||||
|
||||
[node name="PreferencesDialog" type="AcceptDialog"]
|
||||
margin_left = -3.0
|
||||
margin_top = 9.0
|
||||
margin_right = 419.0
|
||||
margin_bottom = 1163.0
|
||||
rect_min_size = Vector2( 422, 340 )
|
||||
window_title = "Preferences"
|
||||
resizable = true
|
||||
script = ExtResource( 1 )
|
||||
__meta__ = {
|
||||
"_edit_horizontal_guides_": [ ],
|
||||
"_edit_use_anchors_": false,
|
||||
"_edit_vertical_guides_": [ ]
|
||||
}
|
||||
|
||||
[node name="HSplitContainer" type="HSplitContainer" parent="."]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
margin_left = 8.0
|
||||
margin_top = 8.0
|
||||
margin_right = -8.0
|
||||
margin_bottom = -36.0
|
||||
size_flags_horizontal = 3
|
||||
custom_constants/autohide = 0
|
||||
split_offset = 1
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Tree" type="Tree" parent="HSplitContainer"]
|
||||
margin_right = 86.0
|
||||
margin_bottom = 1110.0
|
||||
rect_min_size = Vector2( 85, 0 )
|
||||
custom_constants/item_margin = -2
|
||||
hide_root = true
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="HSplitContainer"]
|
||||
margin_left = 98.0
|
||||
margin_right = 406.0
|
||||
margin_bottom = 1110.0
|
||||
rect_min_size = Vector2( 100, 0 )
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="HSplitContainer/ScrollContainer"]
|
||||
margin_right = 308.0
|
||||
margin_bottom = 1230.0
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="General" type="VBoxContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer"]
|
||||
margin_right = 306.0
|
||||
margin_bottom = 180.0
|
||||
|
||||
[node name="SmoothZoom" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/General"]
|
||||
margin_right = 306.0
|
||||
margin_bottom = 24.0
|
||||
hint_tooltip = "Adds a smoother transition when zooming in or out"
|
||||
mouse_default_cursor_shape = 2
|
||||
pressed = true
|
||||
text = "Smooth Zoom"
|
||||
|
||||
[node name="HSeparator2" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/General"]
|
||||
margin_top = 28.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 32.0
|
||||
|
||||
[node name="GridContainer" type="GridContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer/General"]
|
||||
margin_top = 36.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 88.0
|
||||
custom_constants/vseparation = 4
|
||||
custom_constants/hseparation = 4
|
||||
columns = 2
|
||||
|
||||
[node name="LeftIndicatorCheckbox" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/General/GridContainer"]
|
||||
margin_right = 147.0
|
||||
margin_bottom = 24.0
|
||||
hint_tooltip = "Show left mouse pixel indicator or brush on the canvas when drawing"
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
pressed = true
|
||||
text = "Left pixel indicator"
|
||||
|
||||
[node name="RightIndicatorCheckbox" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/General/GridContainer"]
|
||||
margin_left = 151.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 24.0
|
||||
hint_tooltip = "Show right mouse pixel indicator or brush on the canvas when drawing"
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Right pixel indicator"
|
||||
|
||||
[node name="LeftToolIconCheckbox" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/General/GridContainer"]
|
||||
margin_top = 28.0
|
||||
margin_right = 147.0
|
||||
margin_bottom = 52.0
|
||||
hint_tooltip = "Displays an icon of the selected left tool next to the cursor on the canvas"
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
pressed = true
|
||||
text = "Show left tool icon"
|
||||
|
||||
[node name="RightToolIconCheckbox" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/General/GridContainer"]
|
||||
margin_left = 151.0
|
||||
margin_top = 28.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 52.0
|
||||
hint_tooltip = "Displays an icon of the selected right tool next to the cursor on the canvas"
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
pressed = true
|
||||
text = "Show right tool icon"
|
||||
|
||||
[node name="HSeparator3" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/General"]
|
||||
margin_top = 92.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 96.0
|
||||
|
||||
[node name="PressureSentivity" type="HBoxContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer/General"]
|
||||
visible = false
|
||||
margin_top = 116.0
|
||||
margin_right = 334.0
|
||||
margin_bottom = 136.0
|
||||
|
||||
[node name="PressureSensitivityLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/General/PressureSentivity"]
|
||||
margin_top = 3.0
|
||||
margin_right = 173.0
|
||||
margin_bottom = 17.0
|
||||
text = "Tablet pressure sensitivity:"
|
||||
|
||||
[node name="PressureSensitivityOptionButton" type="OptionButton" parent="HSplitContainer/ScrollContainer/VBoxContainer/General/PressureSentivity"]
|
||||
margin_left = 177.0
|
||||
margin_right = 334.0
|
||||
margin_bottom = 20.0
|
||||
text = "Affect Brush's Alpha"
|
||||
items = [ "None", null, false, 0, null, "Affect Brush's Alpha", null, false, 1, null ]
|
||||
selected = 1
|
||||
|
||||
[node name="OpenLastProject" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/General"]
|
||||
margin_top = 100.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 124.0
|
||||
hint_tooltip = "Opens last opened project on startup"
|
||||
mouse_default_cursor_shape = 2
|
||||
pressed = true
|
||||
text = "Open last project on startup"
|
||||
|
||||
[node name="EnableAutosave" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/General"]
|
||||
margin_top = 128.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 152.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Enable autosave"
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="AutosaveInterval" type="HBoxContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer/General"]
|
||||
margin_top = 156.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 180.0
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="AutosaveIntervalLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/General/AutosaveInterval"]
|
||||
margin_top = 5.0
|
||||
margin_right = 115.0
|
||||
margin_bottom = 19.0
|
||||
text = "Autosave interval:"
|
||||
|
||||
[node name="AutosaveInterval" type="SpinBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/General/AutosaveInterval"]
|
||||
margin_left = 119.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 24.0
|
||||
size_flags_horizontal = 3
|
||||
min_value = 1.0
|
||||
max_value = 30.0
|
||||
value = 1.0
|
||||
align = 2
|
||||
suffix = "minute(s)"
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Languages" type="VBoxContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer"]
|
||||
margin_top = 184.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 576.0
|
||||
|
||||
[node name="System Language" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/Languages"]
|
||||
margin_right = 306.0
|
||||
margin_bottom = 24.0
|
||||
mouse_default_cursor_shape = 2
|
||||
pressed = true
|
||||
text = "System Language"
|
||||
|
||||
[node name="German" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/Languages"]
|
||||
margin_top = 28.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 52.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Deutsch [de]"
|
||||
|
||||
[node name="Greek" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/Languages"]
|
||||
margin_top = 56.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 80.0
|
||||
mouse_default_cursor_shape = 2
|
||||
custom_fonts/font = ExtResource( 2 )
|
||||
text = "Ελληνικά [el]"
|
||||
|
||||
[node name="English" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/Languages"]
|
||||
margin_top = 84.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 108.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "English [en]"
|
||||
|
||||
[node name="Esperanto" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/Languages"]
|
||||
margin_top = 112.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 136.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Esperanto [eo]"
|
||||
|
||||
[node name="Spanish" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/Languages"]
|
||||
margin_top = 140.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 164.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Español [es]"
|
||||
|
||||
[node name="French" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/Languages"]
|
||||
margin_top = 168.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 192.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Français [fr]"
|
||||
|
||||
[node name="Italian" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/Languages"]
|
||||
margin_top = 196.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 220.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Italiano [it]"
|
||||
|
||||
[node name="Latvian" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/Languages"]
|
||||
margin_top = 224.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 248.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Latvian [lv]"
|
||||
|
||||
[node name="Polish" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/Languages"]
|
||||
margin_top = 252.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 276.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Polski [pl]"
|
||||
|
||||
[node name="Brazilian Portuguese" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/Languages"]
|
||||
margin_top = 280.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 304.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Português Brasileiro [pt_BR]"
|
||||
|
||||
[node name="Russian" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/Languages"]
|
||||
margin_top = 308.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 332.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Русский [ru]"
|
||||
|
||||
[node name="Chinese Simplified" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/Languages"]
|
||||
margin_top = 336.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 362.0
|
||||
mouse_default_cursor_shape = 2
|
||||
custom_fonts/font = ExtResource( 3 )
|
||||
text = "简体中文 [zh_CN]"
|
||||
|
||||
[node name="Chinese Traditional" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/Languages"]
|
||||
margin_top = 366.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 392.0
|
||||
mouse_default_cursor_shape = 2
|
||||
custom_fonts/font = ExtResource( 3 )
|
||||
text = "繁體中文 [zh_TW]"
|
||||
|
||||
[node name="Themes" type="VBoxContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer"]
|
||||
margin_top = 580.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 716.0
|
||||
|
||||
[node name="Dark Theme" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/Themes"]
|
||||
margin_right = 306.0
|
||||
margin_bottom = 24.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Dark"
|
||||
|
||||
[node name="Gray Theme" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/Themes"]
|
||||
margin_top = 28.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 52.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Gray"
|
||||
|
||||
[node name="Godot\'s Theme" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/Themes"]
|
||||
margin_top = 56.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 80.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Godot"
|
||||
|
||||
[node name="Gold Theme" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/Themes"]
|
||||
margin_top = 84.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 108.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Gold"
|
||||
|
||||
[node name="Light Theme" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/Themes"]
|
||||
margin_top = 112.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 136.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Light"
|
||||
|
||||
[node name="Canvas" type="VBoxContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer"]
|
||||
margin_top = 720.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 912.0
|
||||
|
||||
[node name="GuideOptions" type="GridContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer/Canvas"]
|
||||
margin_right = 306.0
|
||||
margin_bottom = 20.0
|
||||
custom_constants/vseparation = 4
|
||||
custom_constants/hseparation = 4
|
||||
columns = 2
|
||||
|
||||
[node name="GuideColorLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/GuideOptions"]
|
||||
margin_top = 3.0
|
||||
margin_right = 110.0
|
||||
margin_bottom = 17.0
|
||||
rect_min_size = Vector2( 110, 0 )
|
||||
hint_tooltip = "A color of ruler guides displayed on the canvas"
|
||||
mouse_filter = 0
|
||||
text = "Guides color:"
|
||||
|
||||
[node name="GuideColor" type="ColorPickerButton" parent="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/GuideOptions"]
|
||||
margin_left = 114.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 20.0
|
||||
rect_min_size = Vector2( 64, 20 )
|
||||
hint_tooltip = "A color of ruler guides displayed on the canvas"
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
color = Color( 0.63, 0.13, 0.94, 1 )
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/Canvas"]
|
||||
margin_top = 24.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 28.0
|
||||
|
||||
[node name="GridOptions" type="GridContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer/Canvas"]
|
||||
margin_top = 32.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 108.0
|
||||
custom_constants/vseparation = 4
|
||||
custom_constants/hseparation = 4
|
||||
columns = 2
|
||||
|
||||
[node name="WidthLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/GridOptions"]
|
||||
margin_top = 5.0
|
||||
margin_right = 110.0
|
||||
margin_bottom = 19.0
|
||||
rect_min_size = Vector2( 110, 0 )
|
||||
hint_tooltip = "Sets how far apart are vertical lines of the grid"
|
||||
mouse_filter = 0
|
||||
text = "Grid width:"
|
||||
|
||||
[node name="GridWidthValue" type="SpinBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/GridOptions"]
|
||||
margin_left = 114.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 24.0
|
||||
hint_tooltip = "Sets how far apart are vertical lines of the grid"
|
||||
mouse_default_cursor_shape = 2
|
||||
min_value = 1.0
|
||||
max_value = 16384.0
|
||||
value = 1.0
|
||||
align = 2
|
||||
suffix = "px"
|
||||
|
||||
[node name="Height" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/GridOptions"]
|
||||
margin_top = 33.0
|
||||
margin_right = 110.0
|
||||
margin_bottom = 47.0
|
||||
hint_tooltip = "Sets how far apart are horizontal lines of the grid"
|
||||
mouse_filter = 0
|
||||
text = "Grid height:"
|
||||
|
||||
[node name="GridHeightValue" type="SpinBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/GridOptions"]
|
||||
margin_left = 114.0
|
||||
margin_top = 28.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 52.0
|
||||
hint_tooltip = "Sets how far apart are horizontal lines of the grid"
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
min_value = 1.0
|
||||
max_value = 16384.0
|
||||
value = 1.0
|
||||
align = 2
|
||||
suffix = "px"
|
||||
|
||||
[node name="GridColorLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/GridOptions"]
|
||||
margin_top = 59.0
|
||||
margin_right = 110.0
|
||||
margin_bottom = 73.0
|
||||
hint_tooltip = "A color of the grid"
|
||||
mouse_filter = 0
|
||||
text = "Grid color:"
|
||||
|
||||
[node name="GridColor" type="ColorPickerButton" parent="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/GridOptions"]
|
||||
margin_left = 114.0
|
||||
margin_top = 56.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 76.0
|
||||
rect_min_size = Vector2( 64, 20 )
|
||||
hint_tooltip = "A color of the grid"
|
||||
mouse_default_cursor_shape = 2
|
||||
|
||||
[node name="HSeparator2" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/Canvas"]
|
||||
margin_top = 112.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 116.0
|
||||
|
||||
[node name="CheckerOptions" type="GridContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer/Canvas"]
|
||||
margin_top = 120.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 192.0
|
||||
custom_constants/vseparation = 4
|
||||
custom_constants/hseparation = 4
|
||||
columns = 2
|
||||
|
||||
[node name="SizeLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/CheckerOptions"]
|
||||
margin_top = 5.0
|
||||
margin_right = 110.0
|
||||
margin_bottom = 19.0
|
||||
rect_min_size = Vector2( 110, 0 )
|
||||
hint_tooltip = "Size of the transparent checker background"
|
||||
mouse_filter = 0
|
||||
text = "Checker size:"
|
||||
|
||||
[node name="CheckerSizeValue" type="SpinBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/CheckerOptions"]
|
||||
margin_left = 114.0
|
||||
margin_right = 188.0
|
||||
margin_bottom = 24.0
|
||||
hint_tooltip = "Size of the transparent checker background"
|
||||
mouse_default_cursor_shape = 2
|
||||
min_value = 1.0
|
||||
max_value = 16384.0
|
||||
value = 10.0
|
||||
align = 2
|
||||
suffix = "px"
|
||||
|
||||
[node name="CheckerColor1Label" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/CheckerOptions"]
|
||||
margin_top = 31.0
|
||||
margin_right = 110.0
|
||||
margin_bottom = 45.0
|
||||
hint_tooltip = "First color of the transparent checker background"
|
||||
mouse_filter = 0
|
||||
text = "Checker color 1:"
|
||||
|
||||
[node name="CheckerColor1" type="ColorPickerButton" parent="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/CheckerOptions"]
|
||||
margin_left = 114.0
|
||||
margin_top = 28.0
|
||||
margin_right = 188.0
|
||||
margin_bottom = 48.0
|
||||
rect_min_size = Vector2( 64, 20 )
|
||||
hint_tooltip = "First color of the transparent checker background"
|
||||
mouse_default_cursor_shape = 2
|
||||
color = Color( 0.7, 0.7, 0.7, 1 )
|
||||
|
||||
[node name="CheckerColor2Label" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/CheckerOptions"]
|
||||
margin_top = 55.0
|
||||
margin_right = 110.0
|
||||
margin_bottom = 69.0
|
||||
hint_tooltip = "Second color of the transparent checker background"
|
||||
mouse_filter = 0
|
||||
text = "Checker color 2:"
|
||||
|
||||
[node name="CheckerColor2" type="ColorPickerButton" parent="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/CheckerOptions"]
|
||||
margin_left = 114.0
|
||||
margin_top = 52.0
|
||||
margin_right = 188.0
|
||||
margin_bottom = 72.0
|
||||
rect_min_size = Vector2( 64, 20 )
|
||||
hint_tooltip = "Second color of the transparent checker background"
|
||||
mouse_default_cursor_shape = 2
|
||||
color = Color( 1, 1, 1, 1 )
|
||||
|
||||
[node name="Image" type="VBoxContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer"]
|
||||
margin_top = 916.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 992.0
|
||||
|
||||
[node name="ImageOptions" type="GridContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer/Image"]
|
||||
margin_right = 306.0
|
||||
margin_bottom = 76.0
|
||||
custom_constants/vseparation = 4
|
||||
custom_constants/hseparation = 4
|
||||
columns = 2
|
||||
|
||||
[node name="DefaultWidthLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Image/ImageOptions"]
|
||||
margin_top = 5.0
|
||||
margin_right = 110.0
|
||||
margin_bottom = 19.0
|
||||
rect_min_size = Vector2( 110, 0 )
|
||||
hint_tooltip = "A default width of a new image"
|
||||
mouse_filter = 0
|
||||
text = "Default width:"
|
||||
|
||||
[node name="ImageDefaultWidth" type="SpinBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/Image/ImageOptions"]
|
||||
margin_left = 114.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 24.0
|
||||
hint_tooltip = "A default width of a new image"
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
min_value = 1.0
|
||||
max_value = 16384.0
|
||||
value = 64.0
|
||||
align = 2
|
||||
suffix = "px"
|
||||
|
||||
[node name="DefaultHeightLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Image/ImageOptions"]
|
||||
margin_top = 33.0
|
||||
margin_right = 110.0
|
||||
margin_bottom = 47.0
|
||||
hint_tooltip = "A default height of a new image"
|
||||
mouse_filter = 0
|
||||
text = "Default height:"
|
||||
|
||||
[node name="ImageDefaultHeight" type="SpinBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/Image/ImageOptions"]
|
||||
margin_left = 114.0
|
||||
margin_top = 28.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 52.0
|
||||
hint_tooltip = "A default height of a new image"
|
||||
mouse_default_cursor_shape = 2
|
||||
min_value = 1.0
|
||||
max_value = 16384.0
|
||||
value = 64.0
|
||||
align = 2
|
||||
suffix = "px"
|
||||
|
||||
[node name="DefaultFillColorLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Image/ImageOptions"]
|
||||
margin_top = 59.0
|
||||
margin_right = 110.0
|
||||
margin_bottom = 73.0
|
||||
hint_tooltip = "A default background color of a new image"
|
||||
mouse_filter = 0
|
||||
text = "Default fill color:"
|
||||
|
||||
[node name="DefaultFillColor" type="ColorPickerButton" parent="HSplitContainer/ScrollContainer/VBoxContainer/Image/ImageOptions"]
|
||||
margin_left = 114.0
|
||||
margin_top = 56.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 76.0
|
||||
rect_min_size = Vector2( 64, 20 )
|
||||
hint_tooltip = "A default background color of a new image"
|
||||
mouse_default_cursor_shape = 2
|
||||
color = Color( 0, 0, 0, 0 )
|
||||
|
||||
[node name="Shortcuts" type="VBoxContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer"]
|
||||
margin_top = 996.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 1230.0
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts"]
|
||||
margin_right = 306.0
|
||||
margin_bottom = 20.0
|
||||
hint_tooltip = "Only custom preset can be modified"
|
||||
|
||||
[node name="Label" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/HBoxContainer"]
|
||||
margin_top = 3.0
|
||||
margin_right = 45.0
|
||||
margin_bottom = 17.0
|
||||
text = "Preset:"
|
||||
|
||||
[node name="PresetOptionButton" type="OptionButton" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/HBoxContainer"]
|
||||
margin_left = 49.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 20.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Default"
|
||||
items = [ "Default", null, false, 0, null, "Custom", null, false, 1, null ]
|
||||
selected = 0
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts"]
|
||||
margin_top = 24.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 28.0
|
||||
|
||||
[node name="Shortcuts" type="GridContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts"]
|
||||
margin_top = 32.0
|
||||
margin_right = 306.0
|
||||
margin_bottom = 234.0
|
||||
custom_constants/vseparation = 2
|
||||
custom_constants/hseparation = 5
|
||||
columns = 3
|
||||
|
||||
[node name="Empty" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_right = 137.0
|
||||
margin_bottom = 14.0
|
||||
|
||||
[node name="LeftToolLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_left = 142.0
|
||||
margin_right = 221.0
|
||||
margin_bottom = 14.0
|
||||
hint_tooltip = "A tool assigned to the left mouse button"
|
||||
mouse_filter = 0
|
||||
text = "Left Tool:"
|
||||
align = 1
|
||||
|
||||
[node name="RightToolLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_left = 226.0
|
||||
margin_right = 305.0
|
||||
margin_bottom = 14.0
|
||||
hint_tooltip = "A tool assigned to the right mouse button"
|
||||
mouse_filter = 0
|
||||
text = "Right Tool:"
|
||||
align = 1
|
||||
|
||||
[node name="Empty2" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_top = 16.0
|
||||
margin_right = 137.0
|
||||
margin_bottom = 20.0
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
visible = false
|
||||
margin_top = 18.0
|
||||
margin_right = 137.0
|
||||
margin_bottom = 22.0
|
||||
|
||||
[node name="HSeparator2" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_left = 142.0
|
||||
margin_top = 16.0
|
||||
margin_right = 221.0
|
||||
margin_bottom = 20.0
|
||||
|
||||
[node name="HSeparator3" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_left = 226.0
|
||||
margin_top = 16.0
|
||||
margin_right = 305.0
|
||||
margin_bottom = 20.0
|
||||
|
||||
[node name="RectSelectLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_top = 25.0
|
||||
margin_right = 137.0
|
||||
margin_bottom = 39.0
|
||||
text = "Rectangular Selection"
|
||||
|
||||
[node name="left_rectangle_select_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_left = 142.0
|
||||
margin_top = 22.0
|
||||
margin_right = 221.0
|
||||
margin_bottom = 42.0
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="right_rectangle_select_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_left = 226.0
|
||||
margin_top = 22.0
|
||||
margin_right = 305.0
|
||||
margin_bottom = 42.0
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="ZoomLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_top = 47.0
|
||||
margin_right = 137.0
|
||||
margin_bottom = 61.0
|
||||
text = "Zoom"
|
||||
|
||||
[node name="left_zoom_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_left = 142.0
|
||||
margin_top = 44.0
|
||||
margin_right = 221.0
|
||||
margin_bottom = 64.0
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="right_zoom_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_left = 226.0
|
||||
margin_top = 44.0
|
||||
margin_right = 305.0
|
||||
margin_bottom = 64.0
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="ColorPickerLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_top = 69.0
|
||||
margin_right = 137.0
|
||||
margin_bottom = 83.0
|
||||
text = "Color Picker"
|
||||
|
||||
[node name="left_colorpicker_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_left = 142.0
|
||||
margin_top = 66.0
|
||||
margin_right = 221.0
|
||||
margin_bottom = 86.0
|
||||
|
||||
[node name="right_colorpicker_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_left = 226.0
|
||||
margin_top = 66.0
|
||||
margin_right = 305.0
|
||||
margin_bottom = 86.0
|
||||
|
||||
[node name="PencilLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_top = 91.0
|
||||
margin_right = 137.0
|
||||
margin_bottom = 105.0
|
||||
text = "Pencil"
|
||||
|
||||
[node name="left_pencil_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_left = 142.0
|
||||
margin_top = 88.0
|
||||
margin_right = 221.0
|
||||
margin_bottom = 108.0
|
||||
|
||||
[node name="right_pencil_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_left = 226.0
|
||||
margin_top = 88.0
|
||||
margin_right = 305.0
|
||||
margin_bottom = 108.0
|
||||
|
||||
[node name="EraserLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_top = 113.0
|
||||
margin_right = 137.0
|
||||
margin_bottom = 127.0
|
||||
text = "Eraser"
|
||||
|
||||
[node name="left_eraser_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_left = 142.0
|
||||
margin_top = 110.0
|
||||
margin_right = 221.0
|
||||
margin_bottom = 130.0
|
||||
|
||||
[node name="right_eraser_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_left = 226.0
|
||||
margin_top = 110.0
|
||||
margin_right = 305.0
|
||||
margin_bottom = 130.0
|
||||
|
||||
[node name="BucketLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_top = 135.0
|
||||
margin_right = 137.0
|
||||
margin_bottom = 149.0
|
||||
text = "Bucket"
|
||||
|
||||
[node name="left_fill_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_left = 142.0
|
||||
margin_top = 132.0
|
||||
margin_right = 221.0
|
||||
margin_bottom = 152.0
|
||||
|
||||
[node name="right_fill_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_left = 226.0
|
||||
margin_top = 132.0
|
||||
margin_right = 305.0
|
||||
margin_bottom = 152.0
|
||||
|
||||
[node name="LightenDarkenLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_top = 157.0
|
||||
margin_right = 137.0
|
||||
margin_bottom = 171.0
|
||||
text = "Lighten/Darken"
|
||||
|
||||
[node name="left_lightdark_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_left = 142.0
|
||||
margin_top = 154.0
|
||||
margin_right = 221.0
|
||||
margin_bottom = 174.0
|
||||
|
||||
[node name="right_lightdark_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_left = 226.0
|
||||
margin_top = 154.0
|
||||
margin_right = 305.0
|
||||
margin_bottom = 174.0
|
||||
|
||||
[node name="HSeparator4" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_top = 176.0
|
||||
margin_right = 137.0
|
||||
margin_bottom = 180.0
|
||||
|
||||
[node name="HSeparator5" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_left = 142.0
|
||||
margin_top = 176.0
|
||||
margin_right = 221.0
|
||||
margin_bottom = 180.0
|
||||
|
||||
[node name="HSeparator6" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_left = 226.0
|
||||
margin_top = 176.0
|
||||
margin_right = 305.0
|
||||
margin_bottom = 180.0
|
||||
|
||||
[node name="Switch Colors" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_top = 185.0
|
||||
margin_right = 137.0
|
||||
margin_bottom = 199.0
|
||||
text = "Switch Colors"
|
||||
|
||||
[node name="switch_colors" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
|
||||
margin_left = 142.0
|
||||
margin_top = 182.0
|
||||
margin_right = 221.0
|
||||
margin_bottom = 202.0
|
||||
|
||||
[node name="Popups" type="Node" parent="."]
|
||||
|
||||
[node name="ShortcutSelector" type="ConfirmationDialog" parent="Popups"]
|
||||
margin_right = 250.0
|
||||
margin_bottom = 87.5
|
||||
rect_min_size = Vector2( 250, 87.5 )
|
||||
window_title = "Set the shortcut"
|
||||
dialog_text = "Press a key or a key combination to set the shortcut"
|
||||
dialog_hide_on_ok = false
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="EnteredShortcut" type="Label" parent="Popups/ShortcutSelector"]
|
||||
margin_left = 8.0
|
||||
margin_top = 22.0
|
||||
margin_right = 341.0
|
||||
margin_bottom = 51.5
|
||||
align = 1
|
||||
valign = 1
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
[connection signal="about_to_show" from="." to="." method="_on_PreferencesDialog_about_to_show"]
|
||||
[connection signal="popup_hide" from="." to="." method="_on_PreferencesDialog_popup_hide"]
|
||||
[connection signal="item_selected" from="HSplitContainer/Tree" to="." method="_on_Tree_item_selected"]
|
||||
[connection signal="pressed" from="HSplitContainer/ScrollContainer/VBoxContainer/General/SmoothZoom" to="." method="_on_SmoothZoom_pressed"]
|
||||
[connection signal="toggled" from="HSplitContainer/ScrollContainer/VBoxContainer/General/GridContainer/LeftIndicatorCheckbox" to="." method="_on_LeftIndicatorCheckbox_toggled"]
|
||||
[connection signal="toggled" from="HSplitContainer/ScrollContainer/VBoxContainer/General/GridContainer/RightIndicatorCheckbox" to="." method="_on_RightIndicatorCheckbox_toggled"]
|
||||
[connection signal="toggled" from="HSplitContainer/ScrollContainer/VBoxContainer/General/GridContainer/LeftToolIconCheckbox" to="." method="_on_LeftToolIconCheckbox_toggled"]
|
||||
[connection signal="toggled" from="HSplitContainer/ScrollContainer/VBoxContainer/General/GridContainer/RightToolIconCheckbox" to="." method="_on_RightToolIconCheckbox_toggled"]
|
||||
[connection signal="item_selected" from="HSplitContainer/ScrollContainer/VBoxContainer/General/PressureSentivity/PressureSensitivityOptionButton" to="." method="_on_PressureSensitivityOptionButton_item_selected"]
|
||||
[connection signal="pressed" from="HSplitContainer/ScrollContainer/VBoxContainer/General/OpenLastProject" to="." method="_on_OpenLastProject_pressed"]
|
||||
[connection signal="toggled" from="HSplitContainer/ScrollContainer/VBoxContainer/General/EnableAutosave" to="." method="_on_EnableAutosave_toggled"]
|
||||
[connection signal="value_changed" from="HSplitContainer/ScrollContainer/VBoxContainer/General/AutosaveInterval/AutosaveInterval" to="." method="_on_AutosaveInterval_value_changed"]
|
||||
[connection signal="color_changed" from="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/GuideOptions/GuideColor" to="." method="_on_GuideColor_color_changed"]
|
||||
[connection signal="value_changed" from="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/GridOptions/GridWidthValue" to="." method="_on_GridWidthValue_value_changed"]
|
||||
[connection signal="value_changed" from="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/GridOptions/GridHeightValue" to="." method="_on_GridHeightValue_value_changed"]
|
||||
[connection signal="color_changed" from="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/GridOptions/GridColor" to="." method="_on_GridColor_color_changed"]
|
||||
[connection signal="value_changed" from="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/CheckerOptions/CheckerSizeValue" to="." method="_on_CheckerSize_value_changed"]
|
||||
[connection signal="color_changed" from="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/CheckerOptions/CheckerColor1" to="." method="_on_CheckerColor1_color_changed"]
|
||||
[connection signal="color_changed" from="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/CheckerOptions/CheckerColor2" to="." method="_on_CheckerColor2_color_changed"]
|
||||
[connection signal="value_changed" from="HSplitContainer/ScrollContainer/VBoxContainer/Image/ImageOptions/ImageDefaultWidth" to="." method="_on_ImageDefaultWidth_value_changed"]
|
||||
[connection signal="value_changed" from="HSplitContainer/ScrollContainer/VBoxContainer/Image/ImageOptions/ImageDefaultHeight" to="." method="_on_ImageDefaultHeight_value_changed"]
|
||||
[connection signal="color_changed" from="HSplitContainer/ScrollContainer/VBoxContainer/Image/ImageOptions/DefaultFillColor" to="." method="_on_DefaultBackground_color_changed"]
|
||||
[connection signal="item_selected" from="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/HBoxContainer/PresetOptionButton" to="." method="_on_PresetOptionButton_item_selected"]
|
||||
[connection signal="confirmed" from="Popups/ShortcutSelector" to="." method="_on_ShortcutSelector_confirmed"]
|
||||
[connection signal="popup_hide" from="Popups/ShortcutSelector" to="." method="_on_ShortcutSelector_popup_hide"]
|
61
src/Dialogs/RotateImage.gd
Normal file
61
src/Dialogs/RotateImage.gd
Normal file
|
@ -0,0 +1,61 @@
|
|||
extends ConfirmationDialog
|
||||
|
||||
var texture : ImageTexture
|
||||
var aux_img : Image
|
||||
var layer : Image
|
||||
|
||||
func _ready() -> void:
|
||||
texture = ImageTexture.new()
|
||||
texture.flags = 0
|
||||
aux_img = Image.new()
|
||||
$VBoxContainer/HBoxContainer2/OptionButton.add_item("Rotxel")
|
||||
$VBoxContainer/HBoxContainer2/OptionButton.add_item("Upscale, Rotate and Downscale")
|
||||
$VBoxContainer/HBoxContainer2/OptionButton.add_item("Nearest neighbour")
|
||||
|
||||
func set_sprite(sprite : Image) -> void:
|
||||
aux_img.copy_from(sprite)
|
||||
layer = sprite
|
||||
texture.create_from_image(aux_img, 0)
|
||||
$VBoxContainer/TextureRect.texture = texture
|
||||
|
||||
|
||||
func _on_HSlider_value_changed(_value) -> void:
|
||||
rotate()
|
||||
$VBoxContainer/HBoxContainer/SpinBox.value = $VBoxContainer/HBoxContainer/HSlider.value
|
||||
|
||||
|
||||
func _on_SpinBox_value_changed(_value):
|
||||
$VBoxContainer/HBoxContainer/HSlider.value = $VBoxContainer/HBoxContainer/SpinBox.value
|
||||
|
||||
|
||||
func _on_RotateImage_confirmed() -> void:
|
||||
Global.canvas.handle_undo("Draw")
|
||||
match $VBoxContainer/HBoxContainer2/OptionButton.text:
|
||||
"Rotxel":
|
||||
Global.rotxel(layer,$VBoxContainer/HBoxContainer/HSlider.value*PI/180)
|
||||
"Nearest neighbour":
|
||||
Global.nn_rotate(layer,$VBoxContainer/HBoxContainer/HSlider.value*PI/180)
|
||||
"Upscale, Rotate and Downscale":
|
||||
Global.fake_rotsprite(layer,$VBoxContainer/HBoxContainer/HSlider.value*PI/180)
|
||||
Global.canvas.handle_redo("Draw")
|
||||
$VBoxContainer/HBoxContainer/HSlider.value = 0
|
||||
|
||||
func rotate() -> void:
|
||||
var sprite : Image = Image.new()
|
||||
sprite.copy_from(aux_img)
|
||||
match $VBoxContainer/HBoxContainer2/OptionButton.text:
|
||||
"Rotxel":
|
||||
Global.rotxel(sprite,$VBoxContainer/HBoxContainer/HSlider.value*PI/180)
|
||||
"Nearest neighbour":
|
||||
Global.nn_rotate(sprite,$VBoxContainer/HBoxContainer/HSlider.value*PI/180)
|
||||
"Upscale, Rotate and Downscale":
|
||||
Global.fake_rotsprite(sprite,$VBoxContainer/HBoxContainer/HSlider.value*PI/180)
|
||||
texture.create_from_image(sprite, 0)
|
||||
|
||||
|
||||
func _on_OptionButton_item_selected(_id) -> void:
|
||||
rotate()
|
||||
|
||||
|
||||
func _on_RotateImage_about_to_show() -> void:
|
||||
$VBoxContainer/HBoxContainer/HSlider.value = 0
|
85
src/Dialogs/RotateImage.tscn
Normal file
85
src/Dialogs/RotateImage.tscn
Normal file
|
@ -0,0 +1,85 @@
|
|||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://src/Dialogs/RotateImage.gd" type="Script" id=1]
|
||||
|
||||
|
||||
[node name="RotateImage" type="ConfirmationDialog"]
|
||||
margin_right = 245.0
|
||||
margin_bottom = 241.0
|
||||
resizable = true
|
||||
script = ExtResource( 1 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
margin_left = 8.0
|
||||
margin_top = 8.0
|
||||
margin_right = -8.0
|
||||
margin_bottom = -36.0
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="VBoxContainer"]
|
||||
margin_right = 229.0
|
||||
margin_bottom = 145.0
|
||||
size_flags_vertical = 3
|
||||
expand = true
|
||||
stretch_mode = 6
|
||||
|
||||
[node name="HBoxContainer2" type="HBoxContainer" parent="VBoxContainer"]
|
||||
margin_top = 149.0
|
||||
margin_right = 229.0
|
||||
margin_bottom = 169.0
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer2"]
|
||||
margin_top = 3.0
|
||||
margin_right = 34.0
|
||||
margin_bottom = 17.0
|
||||
text = "Type:"
|
||||
|
||||
[node name="OptionButton" type="OptionButton" parent="VBoxContainer/HBoxContainer2"]
|
||||
margin_left = 38.0
|
||||
margin_right = 229.0
|
||||
margin_bottom = 20.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
|
||||
margin_top = 173.0
|
||||
margin_right = 229.0
|
||||
margin_bottom = 197.0
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer"]
|
||||
margin_top = 5.0
|
||||
margin_right = 44.0
|
||||
margin_bottom = 19.0
|
||||
text = "Angle: "
|
||||
|
||||
[node name="HSlider" type="HSlider" parent="VBoxContainer/HBoxContainer"]
|
||||
margin_left = 48.0
|
||||
margin_right = 151.0
|
||||
margin_bottom = 24.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
max_value = 359.0
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="SpinBox" type="SpinBox" parent="VBoxContainer/HBoxContainer"]
|
||||
margin_left = 155.0
|
||||
margin_right = 229.0
|
||||
margin_bottom = 24.0
|
||||
mouse_default_cursor_shape = 2
|
||||
max_value = 359.0
|
||||
[connection signal="about_to_show" from="." to="." method="_on_RotateImage_about_to_show"]
|
||||
[connection signal="confirmed" from="." to="." method="_on_RotateImage_confirmed"]
|
||||
[connection signal="item_selected" from="VBoxContainer/HBoxContainer2/OptionButton" to="." method="_on_OptionButton_item_selected"]
|
||||
[connection signal="value_changed" from="VBoxContainer/HBoxContainer/HSlider" to="." method="_on_HSlider_value_changed"]
|
||||
[connection signal="value_changed" from="VBoxContainer/HBoxContainer/SpinBox" to="." method="_on_SpinBox_value_changed"]
|
21
src/Dialogs/ScaleImage.gd
Normal file
21
src/Dialogs/ScaleImage.gd
Normal file
|
@ -0,0 +1,21 @@
|
|||
extends ConfirmationDialog
|
||||
|
||||
|
||||
func _on_ScaleImage_confirmed() -> void:
|
||||
var width : int = $VBoxContainer/OptionsContainer/WidthValue.value
|
||||
var height : int = $VBoxContainer/OptionsContainer/HeightValue.value
|
||||
var interpolation : int = $VBoxContainer/OptionsContainer/InterpolationType.selected
|
||||
Global.undos += 1
|
||||
Global.undo_redo.create_action("Scale")
|
||||
Global.undo_redo.add_do_property(Global.canvas, "size", Vector2(width, height).floor())
|
||||
|
||||
for i in range(Global.canvas.layers.size() - 1, -1, -1):
|
||||
var sprite : Image = Global.canvas.layers[i][1].get_data()
|
||||
sprite.resize(width, height, interpolation)
|
||||
Global.undo_redo.add_do_property(Global.canvas.layers[i][0], "data", sprite.data)
|
||||
Global.undo_redo.add_undo_property(Global.canvas.layers[i][0], "data", Global.canvas.layers[i][0].data)
|
||||
|
||||
Global.undo_redo.add_undo_property(Global.canvas, "size", Global.canvas.size)
|
||||
Global.undo_redo.add_undo_method(Global, "undo", [Global.canvas])
|
||||
Global.undo_redo.add_do_method(Global, "redo", [Global.canvas])
|
||||
Global.undo_redo.commit_action()
|
77
src/Dialogs/ScaleImage.tscn
Normal file
77
src/Dialogs/ScaleImage.tscn
Normal file
|
@ -0,0 +1,77 @@
|
|||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://src/Dialogs/ScaleImage.gd" type="Script" id=1]
|
||||
|
||||
|
||||
[node name="ScaleImage" type="ConfirmationDialog"]
|
||||
margin_right = 200.0
|
||||
margin_bottom = 114.0
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
margin_left = 8.0
|
||||
margin_top = 8.0
|
||||
margin_right = 192.0
|
||||
margin_bottom = 102.0
|
||||
|
||||
[node name="ImageSize" type="Label" parent="VBoxContainer"]
|
||||
margin_right = 184.0
|
||||
margin_bottom = 15.0
|
||||
text = "Image Size"
|
||||
|
||||
[node name="OptionsContainer" type="GridContainer" parent="VBoxContainer"]
|
||||
margin_top = 19.0
|
||||
margin_right = 184.0
|
||||
margin_bottom = 90.0
|
||||
custom_constants/vseparation = 4
|
||||
custom_constants/hseparation = 2
|
||||
columns = 2
|
||||
|
||||
[node name="WidthLabel" type="Label" parent="VBoxContainer/OptionsContainer"]
|
||||
margin_top = 5.0
|
||||
margin_right = 72.0
|
||||
margin_bottom = 20.0
|
||||
text = "Width:"
|
||||
|
||||
[node name="WidthValue" type="SpinBox" parent="VBoxContainer/OptionsContainer"]
|
||||
margin_left = 72.0
|
||||
margin_right = 155.0
|
||||
margin_bottom = 25.0
|
||||
mouse_default_cursor_shape = 2
|
||||
min_value = 1.0
|
||||
max_value = 16384.0
|
||||
value = 64.0
|
||||
suffix = "px"
|
||||
|
||||
[node name="Height" type="Label" parent="VBoxContainer/OptionsContainer"]
|
||||
margin_top = 30.0
|
||||
margin_right = 72.0
|
||||
margin_bottom = 45.0
|
||||
text = "Height:"
|
||||
|
||||
[node name="HeightValue" type="SpinBox" parent="VBoxContainer/OptionsContainer"]
|
||||
margin_left = 72.0
|
||||
margin_top = 25.0
|
||||
margin_right = 155.0
|
||||
margin_bottom = 50.0
|
||||
mouse_default_cursor_shape = 2
|
||||
min_value = 1.0
|
||||
max_value = 16384.0
|
||||
value = 64.0
|
||||
suffix = "px"
|
||||
|
||||
[node name="InterpolationLabel" type="Label" parent="VBoxContainer/OptionsContainer"]
|
||||
margin_top = 53.0
|
||||
margin_right = 72.0
|
||||
margin_bottom = 68.0
|
||||
text = "Interpolation:"
|
||||
|
||||
[node name="InterpolationType" type="OptionButton" parent="VBoxContainer/OptionsContainer"]
|
||||
margin_left = 72.0
|
||||
margin_top = 50.0
|
||||
margin_right = 155.0
|
||||
margin_bottom = 71.0
|
||||
text = "Nearest"
|
||||
items = [ "Nearest", null, false, 0, null, "Bilinear", null, false, 1, null, "Cubic", null, false, 2, null, "Trilinear", null, false, 3, null, "Lanczos", null, false, 4, null ]
|
||||
selected = 0
|
||||
[connection signal="confirmed" from="." to="." method="_on_ScaleImage_confirmed"]
|
75
src/Dialogs/SplashDialog.gd
Normal file
75
src/Dialogs/SplashDialog.gd
Normal file
|
@ -0,0 +1,75 @@
|
|||
extends WindowDialog
|
||||
|
||||
onready var changes_label : Label = $"Contents/HBoxContainer/Buttons_Changelog/VBoxContainer/Changlog/ChangesLabel"
|
||||
onready var art_by_label : Label = $"Contents/HBoxContainer/Logo_ArtWork/CenterContainer/ArtContainer/ArtCredits"
|
||||
onready var show_on_startup_button : CheckBox = $"Contents/MarginContainer/Info/VBoxContainer/HBoxContainer/ShowOnStartup"
|
||||
onready var developed_by_label : Label = $"Contents/MarginContainer/Info/VBoxContainer/Branding/VBoxContainer/DevelopedBy"
|
||||
onready var platinum_placeholder_label : Label = $"Contents/MarginContainer/Info/Sponsors/PlatinumContainer/PlaceholderLabel"
|
||||
onready var gold_placeholder_label : Label = $"Contents/MarginContainer/Info/Sponsors/GoldContainer/PlaceholderLabel"
|
||||
|
||||
|
||||
func _on_SplashDialog_about_to_show() -> void:
|
||||
if Global.config_cache.has_section_key("preferences", "startup"):
|
||||
show_on_startup_button.pressed = !Global.config_cache.get_value("preferences", "startup")
|
||||
var current_version : String = ProjectSettings.get_setting("application/config/Version")
|
||||
window_title = "Pixelorama" + " " + current_version
|
||||
changes_label.text = current_version + " " + tr("Changes")
|
||||
|
||||
art_by_label.text = tr("Art by") + ": Erevos"
|
||||
if "zh" in TranslationServer.get_locale():
|
||||
show_on_startup_button.add_font_override("font", preload("res://Assets/Fonts/CJK/NotoSansCJKtc-Small.tres"))
|
||||
developed_by_label.add_font_override("font", preload("res://Assets/Fonts/CJK/NotoSansCJKtc-Small.tres"))
|
||||
platinum_placeholder_label.add_font_override("font", preload("res://Assets/Fonts/CJK/NotoSansCJKtc-Bold.tres"))
|
||||
gold_placeholder_label.add_font_override("font", preload("res://Assets/Fonts/CJK/NotoSansCJKtc-Bold.tres"))
|
||||
else:
|
||||
show_on_startup_button.add_font_override("font", preload("res://Assets/Fonts/Roboto-Small.tres"))
|
||||
developed_by_label.add_font_override("font", preload("res://Assets/Fonts/Roboto-Small.tres"))
|
||||
platinum_placeholder_label.add_font_override("font", preload("res://Assets/Fonts/Roboto-Bold.tres"))
|
||||
gold_placeholder_label.add_font_override("font", preload("res://Assets/Fonts/Roboto-Bold.tres"))
|
||||
|
||||
|
||||
func _on_ArtCredits_pressed() -> void:
|
||||
OS.shell_open("https://www.instagram.com/erevoid")
|
||||
|
||||
|
||||
func _on_ShowOnStartup_toggled(pressed : bool) -> void:
|
||||
if pressed:
|
||||
Global.config_cache.set_value("preferences", "startup", false)
|
||||
else:
|
||||
Global.config_cache.set_value("preferences", "startup", true)
|
||||
|
||||
|
||||
func _on_PatronButton_pressed() -> void:
|
||||
OS.shell_open("https://www.patreon.com/OramaInteractive")
|
||||
|
||||
|
||||
func _on_TakeThisSpot_pressed() -> void:
|
||||
OS.shell_open("https://www.patreon.com/OramaInteractive")
|
||||
|
||||
|
||||
func _on_GithubButton_pressed() -> void:
|
||||
OS.shell_open("https://github.com/Orama-Interactive/Pixelorama")
|
||||
|
||||
|
||||
func _on_DiscordButton_pressed() -> void:
|
||||
OS.shell_open("https://discord.gg/GTMtr8s")
|
||||
|
||||
|
||||
func _on_NewBtn_pressed() -> void:
|
||||
Global.control.file_menu_id_pressed(0)
|
||||
visible = false
|
||||
|
||||
|
||||
func _on_OpenBtn__pressed() -> void:
|
||||
Global.control.file_menu_id_pressed(1)
|
||||
visible = false
|
||||
|
||||
|
||||
func _on_OpenLastBtn_pressed() -> void:
|
||||
Global.control.file_menu_id_pressed(2)
|
||||
visible = false
|
||||
|
||||
|
||||
func _on_ImportBtn_pressed() -> void:
|
||||
Global.control.file_menu_id_pressed(5)
|
||||
visible = false
|
440
src/Dialogs/SplashDialog.tscn
Normal file
440
src/Dialogs/SplashDialog.tscn
Normal file
|
@ -0,0 +1,440 @@
|
|||
[gd_scene load_steps=12 format=2]
|
||||
|
||||
[ext_resource path="res://src/Dialogs/SplashDialog.gd" type="Script" id=1]
|
||||
[ext_resource path="res://Assets/Graphics/Pixelorama Logo.png" type="Texture" id=2]
|
||||
[ext_resource path="res://Assets/Graphics/Become a patron.png" type="Texture" id=3]
|
||||
[ext_resource path="res://Assets/Graphics/Become a patron_Hover.png" type="Texture" id=4]
|
||||
[ext_resource path="res://Assets/Graphics/Splash Art.png" type="Texture" id=5]
|
||||
[ext_resource path="res://Assets/Fonts/Roboto-Bold.tres" type="DynamicFont" id=6]
|
||||
[ext_resource path="res://Assets/Fonts/Roboto-Small.tres" type="DynamicFont" id=7]
|
||||
[ext_resource path="res://Assets/Graphics/orama_64x64.png" type="Texture" id=8]
|
||||
[ext_resource path="res://Assets/Graphics/discord.png" type="Texture" id=9]
|
||||
[ext_resource path="res://Assets/Graphics/GitHub-32px.png" type="Texture" id=10]
|
||||
[ext_resource path="res://Assets/Graphics/Patreon_Mark_White.png" type="Texture" id=11]
|
||||
|
||||
|
||||
[node name="SplashDialog" type="WindowDialog"]
|
||||
margin_right = 614.0
|
||||
margin_bottom = 590.0
|
||||
rect_min_size = Vector2( 600, 560 )
|
||||
script = ExtResource( 1 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Contents" type="VBoxContainer" parent="."]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
custom_constants/separation = 0
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="Contents"]
|
||||
margin_right = 617.0
|
||||
margin_bottom = 436.0
|
||||
custom_constants/separation = 0
|
||||
|
||||
[node name="Logo_ArtWork" type="VBoxContainer" parent="Contents/HBoxContainer"]
|
||||
margin_right = 350.0
|
||||
margin_bottom = 436.0
|
||||
rect_min_size = Vector2( 350, 0 )
|
||||
custom_constants/separation = 15
|
||||
|
||||
[node name="PixeloramaLogo" type="TextureRect" parent="Contents/HBoxContainer/Logo_ArtWork"]
|
||||
margin_right = 350.0
|
||||
margin_bottom = 124.0
|
||||
rect_min_size = Vector2( 0, 80 )
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
texture = ExtResource( 2 )
|
||||
stretch_mode = 6
|
||||
|
||||
[node name="CenterContainer" type="MarginContainer" parent="Contents/HBoxContainer/Logo_ArtWork"]
|
||||
margin_top = 139.0
|
||||
margin_right = 350.0
|
||||
margin_bottom = 436.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 0
|
||||
|
||||
[node name="ArtContainer" type="VBoxContainer" parent="Contents/HBoxContainer/Logo_ArtWork/CenterContainer"]
|
||||
margin_right = 350.0
|
||||
margin_bottom = 297.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
custom_constants/separation = 17
|
||||
|
||||
[node name="SplashArt" type="TextureButton" parent="Contents/HBoxContainer/Logo_ArtWork/CenterContainer/ArtContainer"]
|
||||
margin_left = 45.0
|
||||
margin_right = 305.0
|
||||
margin_bottom = 260.0
|
||||
rect_min_size = Vector2( 260, 260 )
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 6
|
||||
texture_normal = ExtResource( 5 )
|
||||
expand = true
|
||||
stretch_mode = 5
|
||||
|
||||
[node name="ArtCredits" type="Button" parent="Contents/HBoxContainer/Logo_ArtWork/CenterContainer/ArtContainer"]
|
||||
margin_top = 277.0
|
||||
margin_right = 350.0
|
||||
margin_bottom = 297.0
|
||||
mouse_default_cursor_shape = 2
|
||||
custom_constants/hseparation = 0
|
||||
text = "Art by Erevoid"
|
||||
flat = true
|
||||
|
||||
[node name="VSeparator" type="VSeparator" parent="Contents/HBoxContainer"]
|
||||
margin_left = 350.0
|
||||
margin_right = 354.0
|
||||
margin_bottom = 436.0
|
||||
|
||||
[node name="Buttons_Changelog" type="MarginContainer" parent="Contents/HBoxContainer"]
|
||||
margin_left = 354.0
|
||||
margin_right = 617.0
|
||||
margin_bottom = 436.0
|
||||
size_flags_horizontal = 3
|
||||
custom_constants/margin_right = 10
|
||||
custom_constants/margin_top = 10
|
||||
custom_constants/margin_left = 10
|
||||
custom_constants/margin_bottom = 10
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="Contents/HBoxContainer/Buttons_Changelog"]
|
||||
margin_left = 10.0
|
||||
margin_top = 10.0
|
||||
margin_right = 253.0
|
||||
margin_bottom = 426.0
|
||||
size_flags_horizontal = 3
|
||||
custom_constants/separation = 10
|
||||
|
||||
[node name="Buttons" type="VBoxContainer" parent="Contents/HBoxContainer/Buttons_Changelog/VBoxContainer"]
|
||||
margin_right = 243.0
|
||||
margin_bottom = 110.0
|
||||
size_flags_vertical = 3
|
||||
custom_constants/separation = 10
|
||||
|
||||
[node name="NewBtn" type="Button" parent="Contents/HBoxContainer/Buttons_Changelog/VBoxContainer/Buttons"]
|
||||
margin_right = 243.0
|
||||
margin_bottom = 20.0
|
||||
text = "New"
|
||||
|
||||
[node name="OpenBtn " type="Button" parent="Contents/HBoxContainer/Buttons_Changelog/VBoxContainer/Buttons"]
|
||||
margin_top = 30.0
|
||||
margin_right = 243.0
|
||||
margin_bottom = 50.0
|
||||
text = "Open"
|
||||
|
||||
[node name="OpenLastBtn" type="Button" parent="Contents/HBoxContainer/Buttons_Changelog/VBoxContainer/Buttons"]
|
||||
margin_top = 60.0
|
||||
margin_right = 243.0
|
||||
margin_bottom = 80.0
|
||||
text = "Open Last Project"
|
||||
|
||||
[node name="ImportBtn" type="Button" parent="Contents/HBoxContainer/Buttons_Changelog/VBoxContainer/Buttons"]
|
||||
margin_top = 90.0
|
||||
margin_right = 243.0
|
||||
margin_bottom = 110.0
|
||||
text = "Import"
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="Contents/HBoxContainer/Buttons_Changelog/VBoxContainer"]
|
||||
margin_top = 120.0
|
||||
margin_right = 243.0
|
||||
margin_bottom = 124.0
|
||||
|
||||
[node name="Changlog" type="VBoxContainer" parent="Contents/HBoxContainer/Buttons_Changelog/VBoxContainer"]
|
||||
margin_top = 134.0
|
||||
margin_right = 243.0
|
||||
margin_bottom = 416.0
|
||||
size_flags_vertical = 3
|
||||
custom_constants/separation = 8
|
||||
|
||||
[node name="ChangesLabel" type="Label" parent="Contents/HBoxContainer/Buttons_Changelog/VBoxContainer/Changlog"]
|
||||
margin_right = 243.0
|
||||
margin_bottom = 14.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 8
|
||||
text = "v0.6 Changes"
|
||||
|
||||
[node name="ChangelogContainer" type="VBoxContainer" parent="Contents/HBoxContainer/Buttons_Changelog/VBoxContainer/Changlog"]
|
||||
margin_top = 22.0
|
||||
margin_right = 243.0
|
||||
margin_bottom = 282.0
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="ChangelogScroll" type="ScrollContainer" parent="Contents/HBoxContainer/Buttons_Changelog/VBoxContainer/Changlog/ChangelogContainer"]
|
||||
margin_right = 243.0
|
||||
margin_bottom = 260.0
|
||||
rect_min_size = Vector2( 0, 260 )
|
||||
|
||||
[node name="Label" type="Label" parent="Contents/HBoxContainer/Buttons_Changelog/VBoxContainer/Changlog/ChangelogContainer/ChangelogScroll"]
|
||||
margin_right = 243.0
|
||||
margin_bottom = 1680.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
text = "Added
|
||||
|
||||
- Image layer rotation! Choose between 2 rotation algorithms, Rotxel and Nearest Neighbour - Thanks to azagaya!
|
||||
- Crowdin integration for contributing translations!
|
||||
- Spanish translation - thanks to azagaya & Lilly And!
|
||||
- Chinese Simplified translation - thanks to Chenxu Wang!
|
||||
- Latvian translation - thanks to Agnis Aldiņš (NeZvers)!
|
||||
- Translators can now be seen in the About window.
|
||||
- It is now possible to remove custom brushes with the middle mouse button.
|
||||
- Added HSV mode to the color picker. (Added automatically because of the Godot 3.2 update)
|
||||
- Lanczos scaling interpolation. (Added because of the Godot 3.2 update)
|
||||
- You can now drag and drop (or right click and open with) image and .pxo files in Pixelorama.
|
||||
- You can now hide the animation timeline - Thanks to YeldhamDev!
|
||||
|
||||
Changed
|
||||
|
||||
- Major changes to alpha blending behavior. The alpha values now get added/blended together instead of just replacing the pixel with the new value.
|
||||
- Replaced some OS alerts with a custom made error dialog.
|
||||
- Made the zooming smoother, is toggleable in Preferences whether to keep the new zooming or the old one.
|
||||
- The camera now zooms at the mouse's position.
|
||||
- Made the \"X\" button on the custom brushes a little smaller.
|
||||
- The color picker will now have a small white triangle on the top left of the color preview if at least one of its RGB values are above 1 in Raw mode. (Added automatically because of the Godot 3.2 update)
|
||||
- You can now toggle the visibility of hidden items on and off in the file dialogs. (Added automatically because of the Godot 3.2 update)
|
||||
- The language buttons in the preferences have their localized names in their hint tooltips. For example, if you hover over the \"English\" button while the language is Greek, the hint tooltip will be \"Αγγλικά\", which is the Greek word for English.
|
||||
- Translation updates.
|
||||
- The presets in the ColorPickers are now hidden - Thanks to YeldhamDev!
|
||||
- When opening a project (.pxo file), the save path is being set to the opened project's path - Thanks to YeldhamDev!
|
||||
|
||||
Fixed
|
||||
|
||||
- Delay the splash screen popup so it shows properly centered - Thanks to YeldhamDev!
|
||||
- Possibly fixed crashes with motion drawing and undo/redoing.
|
||||
- Fixed bug (which also caused crashes sometimes) when generating an outline inside the image and it was going outside the canvas' borders.
|
||||
- Fixed crash when importing images that were failing to load. They still fail to load, but Pixelorama does not crash.
|
||||
- Possibly fixed a rare crash where the cursor image was failing to load. It is now being loaded only once.
|
||||
- Fixed ruler markings cutting off before they should - Thanks to YeldhamDev!
|
||||
- Fixed bug where resizing the image on export was not working on Godot 3.2 - Issue #161"
|
||||
autowrap = true
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="Contents"]
|
||||
margin_top = 436.0
|
||||
margin_right = 617.0
|
||||
margin_bottom = 440.0
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="Contents"]
|
||||
margin_top = 440.0
|
||||
margin_right = 617.0
|
||||
margin_bottom = 593.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
custom_constants/margin_right = 10
|
||||
custom_constants/margin_top = 10
|
||||
custom_constants/margin_left = 10
|
||||
custom_constants/margin_bottom = 10
|
||||
|
||||
[node name="Info" type="HBoxContainer" parent="Contents/MarginContainer"]
|
||||
margin_left = 10.0
|
||||
margin_top = 10.0
|
||||
margin_right = 607.0
|
||||
margin_bottom = 143.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
custom_constants/separation = 3
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Sponsors" type="HBoxContainer" parent="Contents/MarginContainer/Info"]
|
||||
margin_right = 362.0
|
||||
margin_bottom = 133.0
|
||||
size_flags_vertical = 3
|
||||
custom_constants/separation = 5
|
||||
|
||||
[node name="PlatinumContainer" type="VBoxContainer" parent="Contents/MarginContainer/Info/Sponsors"]
|
||||
margin_right = 125.0
|
||||
margin_bottom = 133.0
|
||||
rect_min_size = Vector2( 125, 0 )
|
||||
|
||||
[node name="Label" type="Label" parent="Contents/MarginContainer/Info/Sponsors/PlatinumContainer"]
|
||||
margin_right = 125.0
|
||||
margin_bottom = 14.0
|
||||
text = "Platinum Sponsor"
|
||||
align = 1
|
||||
|
||||
[node name="PlaceholderLabel" type="Label" parent="Contents/MarginContainer/Info/Sponsors/PlatinumContainer"]
|
||||
margin_top = 47.0
|
||||
margin_right = 125.0
|
||||
margin_bottom = 80.0
|
||||
size_flags_vertical = 6
|
||||
custom_fonts/font = ExtResource( 6 )
|
||||
custom_colors/font_color = Color( 0.678431, 0.611765, 0.807843, 1 )
|
||||
text = "Platinum
|
||||
Sponsor"
|
||||
align = 1
|
||||
|
||||
[node name="TakeThisSpot" type="Button" parent="Contents/MarginContainer/Info/Sponsors/PlatinumContainer"]
|
||||
margin_top = 113.0
|
||||
margin_right = 125.0
|
||||
margin_bottom = 133.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Take this spot!"
|
||||
flat = true
|
||||
|
||||
[node name="VSeparator" type="VSeparator" parent="Contents/MarginContainer/Info/Sponsors"]
|
||||
margin_left = 130.0
|
||||
margin_right = 134.0
|
||||
margin_bottom = 133.0
|
||||
|
||||
[node name="GoldContainer" type="VBoxContainer" parent="Contents/MarginContainer/Info/Sponsors"]
|
||||
margin_left = 139.0
|
||||
margin_right = 264.0
|
||||
margin_bottom = 133.0
|
||||
rect_min_size = Vector2( 125, 0 )
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="Label" type="Label" parent="Contents/MarginContainer/Info/Sponsors/GoldContainer"]
|
||||
margin_right = 125.0
|
||||
margin_bottom = 14.0
|
||||
text = "Gold Sponsors"
|
||||
align = 1
|
||||
|
||||
[node name="PlaceholderLabel" type="Label" parent="Contents/MarginContainer/Info/Sponsors/GoldContainer"]
|
||||
margin_top = 47.0
|
||||
margin_right = 125.0
|
||||
margin_bottom = 80.0
|
||||
size_flags_vertical = 6
|
||||
custom_fonts/font = ExtResource( 6 )
|
||||
custom_colors/font_color = Color( 0.678431, 0.611765, 0.807843, 1 )
|
||||
text = "Gold
|
||||
Sponsors"
|
||||
align = 1
|
||||
|
||||
[node name="TakeThisSpot" type="Button" parent="Contents/MarginContainer/Info/Sponsors/GoldContainer"]
|
||||
margin_top = 113.0
|
||||
margin_right = 125.0
|
||||
margin_bottom = 133.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Take this spot!"
|
||||
flat = true
|
||||
|
||||
[node name="VSeparator2" type="VSeparator" parent="Contents/MarginContainer/Info/Sponsors"]
|
||||
margin_left = 269.0
|
||||
margin_right = 273.0
|
||||
margin_bottom = 133.0
|
||||
|
||||
[node name="PatronContainer" type="VBoxContainer" parent="Contents/MarginContainer/Info/Sponsors"]
|
||||
margin_left = 278.0
|
||||
margin_right = 362.0
|
||||
margin_bottom = 133.0
|
||||
size_flags_horizontal = 3
|
||||
custom_constants/separation = 10
|
||||
alignment = 1
|
||||
|
||||
[node name="PatronsLabel" type="Label" parent="Contents/MarginContainer/Info/Sponsors/PatronContainer"]
|
||||
margin_right = 84.0
|
||||
margin_bottom = 14.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 8
|
||||
text = "Patrons:"
|
||||
align = 1
|
||||
|
||||
[node name="PatronButton" type="TextureButton" parent="Contents/MarginContainer/Info/Sponsors/PatronContainer"]
|
||||
margin_top = 24.0
|
||||
margin_right = 84.0
|
||||
margin_bottom = 133.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
texture_normal = ExtResource( 3 )
|
||||
texture_hover = ExtResource( 4 )
|
||||
stretch_mode = 5
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="Contents/MarginContainer/Info"]
|
||||
margin_left = 365.0
|
||||
margin_right = 597.0
|
||||
margin_bottom = 133.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
custom_constants/separation = 5
|
||||
alignment = 2
|
||||
|
||||
[node name="Branding" type="HBoxContainer" parent="Contents/MarginContainer/Info/VBoxContainer"]
|
||||
margin_right = 232.0
|
||||
margin_bottom = 104.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
alignment = 2
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="Contents/MarginContainer/Info/VBoxContainer/Branding"]
|
||||
margin_left = 99.0
|
||||
margin_right = 196.0
|
||||
margin_bottom = 104.0
|
||||
size_flags_vertical = 3
|
||||
alignment = 1
|
||||
|
||||
[node name="Logo" type="TextureRect" parent="Contents/MarginContainer/Info/VBoxContainer/Branding/VBoxContainer"]
|
||||
margin_top = 3.0
|
||||
margin_right = 97.0
|
||||
margin_bottom = 67.0
|
||||
texture = ExtResource( 8 )
|
||||
stretch_mode = 4
|
||||
|
||||
[node name="DevelopedBy" type="Label" parent="Contents/MarginContainer/Info/VBoxContainer/Branding/VBoxContainer"]
|
||||
margin_top = 71.0
|
||||
margin_right = 97.0
|
||||
margin_bottom = 84.0
|
||||
custom_fonts/font = ExtResource( 7 )
|
||||
text = "Orama Interactive"
|
||||
align = 1
|
||||
|
||||
[node name="Copyright" type="Label" parent="Contents/MarginContainer/Info/VBoxContainer/Branding/VBoxContainer"]
|
||||
margin_top = 88.0
|
||||
margin_right = 97.0
|
||||
margin_bottom = 101.0
|
||||
custom_fonts/font = ExtResource( 7 )
|
||||
text = "Copyright 2019-2020"
|
||||
align = 1
|
||||
|
||||
[node name="Links" type="VBoxContainer" parent="Contents/MarginContainer/Info/VBoxContainer/Branding"]
|
||||
margin_left = 200.0
|
||||
margin_right = 232.0
|
||||
margin_bottom = 104.0
|
||||
|
||||
[node name="TextureButton" type="TextureButton" parent="Contents/MarginContainer/Info/VBoxContainer/Branding/Links"]
|
||||
margin_right = 32.0
|
||||
margin_bottom = 32.0
|
||||
texture_normal = ExtResource( 10 )
|
||||
|
||||
[node name="TextureButton2" type="TextureButton" parent="Contents/MarginContainer/Info/VBoxContainer/Branding/Links"]
|
||||
margin_top = 36.0
|
||||
margin_right = 32.0
|
||||
margin_bottom = 68.0
|
||||
texture_normal = ExtResource( 9 )
|
||||
|
||||
[node name="TextureButton3" type="TextureButton" parent="Contents/MarginContainer/Info/VBoxContainer/Branding/Links"]
|
||||
margin_top = 72.0
|
||||
margin_right = 32.0
|
||||
margin_bottom = 104.0
|
||||
texture_normal = ExtResource( 11 )
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="Contents/MarginContainer/Info/VBoxContainer"]
|
||||
margin_top = 109.0
|
||||
margin_right = 232.0
|
||||
margin_bottom = 133.0
|
||||
alignment = 2
|
||||
|
||||
[node name="ShowOnStartup" type="CheckBox" parent="Contents/MarginContainer/Info/VBoxContainer/HBoxContainer"]
|
||||
margin_left = 127.0
|
||||
margin_right = 232.0
|
||||
margin_bottom = 24.0
|
||||
mouse_default_cursor_shape = 2
|
||||
custom_fonts/font = ExtResource( 7 )
|
||||
text = "Don't show again"
|
||||
[connection signal="about_to_show" from="." to="." method="_on_SplashDialog_about_to_show"]
|
||||
[connection signal="pressed" from="Contents/HBoxContainer/Logo_ArtWork/CenterContainer/ArtContainer/SplashArt" to="." method="_on_ArtCredits_pressed"]
|
||||
[connection signal="pressed" from="Contents/HBoxContainer/Logo_ArtWork/CenterContainer/ArtContainer/ArtCredits" to="." method="_on_ArtCredits_pressed"]
|
||||
[connection signal="pressed" from="Contents/HBoxContainer/Buttons_Changelog/VBoxContainer/Buttons/NewBtn" to="." method="_on_NewBtn_pressed"]
|
||||
[connection signal="pressed" from="Contents/HBoxContainer/Buttons_Changelog/VBoxContainer/Buttons/OpenBtn " to="." method="_on_OpenBtn__pressed"]
|
||||
[connection signal="pressed" from="Contents/HBoxContainer/Buttons_Changelog/VBoxContainer/Buttons/OpenLastBtn" to="." method="_on_OpenLastBtn_pressed"]
|
||||
[connection signal="pressed" from="Contents/HBoxContainer/Buttons_Changelog/VBoxContainer/Buttons/ImportBtn" to="." method="_on_ImportBtn_pressed"]
|
||||
[connection signal="pressed" from="Contents/MarginContainer/Info/Sponsors/PlatinumContainer/TakeThisSpot" to="." method="_on_TakeThisSpot_pressed"]
|
||||
[connection signal="pressed" from="Contents/MarginContainer/Info/Sponsors/GoldContainer/TakeThisSpot" to="." method="_on_TakeThisSpot_pressed"]
|
||||
[connection signal="pressed" from="Contents/MarginContainer/Info/Sponsors/PatronContainer/PatronButton" to="." method="_on_PatronButton_pressed"]
|
||||
[connection signal="pressed" from="Contents/MarginContainer/Info/VBoxContainer/Branding/Links/TextureButton" to="." method="_on_GithubButton_pressed"]
|
||||
[connection signal="pressed" from="Contents/MarginContainer/Info/VBoxContainer/Branding/Links/TextureButton2" to="." method="_on_DiscordButton_pressed"]
|
||||
[connection signal="pressed" from="Contents/MarginContainer/Info/VBoxContainer/Branding/Links/TextureButton3" to="." method="_on_PatronButton_pressed"]
|
||||
[connection signal="toggled" from="Contents/MarginContainer/Info/VBoxContainer/HBoxContainer/ShowOnStartup" to="." method="_on_ShowOnStartup_toggled"]
|
40
src/Drawers.gd
Normal file
40
src/Drawers.gd
Normal file
|
@ -0,0 +1,40 @@
|
|||
class Drawer:
|
||||
func reset() -> void:
|
||||
pass
|
||||
|
||||
func set_pixel(_sprite: Image, _pos: Vector2, _new_color: Color) -> void:
|
||||
pass
|
||||
|
||||
|
||||
class SimpleDrawer extends Drawer:
|
||||
func reset() -> void:
|
||||
pass
|
||||
|
||||
|
||||
func set_pixel(_sprite: Image, _pos: Vector2, _new_color: Color) -> void:
|
||||
_sprite.set_pixel(_pos.x, _pos.y, _new_color)
|
||||
|
||||
|
||||
class PixelPerfectDrawer extends Drawer:
|
||||
const neighbours = [Vector2(0, 1), Vector2(1, 0), Vector2(-1, 0), Vector2(0, -1)]
|
||||
const corners = [Vector2(1, 1), Vector2(-1, -1), Vector2(-1, 1), Vector2(1, -1)]
|
||||
var last_pixels = [null, null]
|
||||
|
||||
|
||||
func reset():
|
||||
last_pixels = [null, null]
|
||||
|
||||
|
||||
func set_pixel(_sprite: Image, _pos: Vector2, _new_color: Color) -> void:
|
||||
last_pixels.push_back([_pos, _sprite.get_pixel(_pos.x, _pos.y)])
|
||||
_sprite.set_pixel(_pos.x, _pos.y, _new_color)
|
||||
|
||||
var corner = last_pixels.pop_front()
|
||||
var neighbour = last_pixels[0]
|
||||
|
||||
if corner == null or neighbour == null:
|
||||
return
|
||||
|
||||
if _pos - corner[0] in corners and _pos - neighbour[0] in neighbours:
|
||||
_sprite.set_pixel(neighbour[0].x, neighbour[0].y, neighbour[1])
|
||||
last_pixels[0] = corner
|
80
src/LayerButton.gd
Normal file
80
src/LayerButton.gd
Normal file
|
@ -0,0 +1,80 @@
|
|||
class_name LayerButton
|
||||
extends Button
|
||||
|
||||
var i := 0
|
||||
var visibility_button : BaseButton
|
||||
var lock_button : BaseButton
|
||||
var linked_button : BaseButton
|
||||
var label : Label
|
||||
var line_edit : LineEdit
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
visibility_button = Global.find_node_by_name(self, "VisibilityButton")
|
||||
lock_button = Global.find_node_by_name(self, "LockButton")
|
||||
linked_button = Global.find_node_by_name(self, "LinkButton")
|
||||
label = Global.find_node_by_name(self, "Label")
|
||||
line_edit = Global.find_node_by_name(self, "LineEdit")
|
||||
|
||||
if Global.layers[i][1]:
|
||||
visibility_button.texture_normal = load("res://Assets/Graphics/%s Themes/Layers/Layer_Visible.png" % Global.theme_type)
|
||||
visibility_button.texture_hover = load("res://Assets/Graphics/%s Themes/Layers/Layer_Visible_Hover.png" % Global.theme_type)
|
||||
else:
|
||||
visibility_button.texture_normal = load("res://Assets/Graphics/%s Themes/Layers/Layer_Invisible.png" % Global.theme_type)
|
||||
visibility_button.texture_hover = load("res://Assets/Graphics/%s Themes/Layers/Layer_Invisible_Hover.png" % Global.theme_type)
|
||||
|
||||
if Global.layers[i][2]:
|
||||
lock_button.texture_normal = load("res://Assets/Graphics/%s Themes/Layers/Lock.png" % Global.theme_type)
|
||||
lock_button.texture_hover = load("res://Assets/Graphics/%s Themes/Layers/Lock_Hover.png" % Global.theme_type)
|
||||
else:
|
||||
lock_button.texture_normal = load("res://Assets/Graphics/%s Themes/Layers/Unlock.png" % Global.theme_type)
|
||||
lock_button.texture_hover = load("res://Assets/Graphics/%s Themes/Layers/Unlock_Hover.png" % Global.theme_type)
|
||||
|
||||
if Global.layers[i][4]: # If new layers will be linked
|
||||
linked_button.texture_normal = load("res://Assets/Graphics/%s Themes/Layers/Linked_Layer.png" % Global.theme_type)
|
||||
linked_button.texture_hover = load("res://Assets/Graphics/%s Themes/Layers/Linked_Layer_Hover.png" % Global.theme_type)
|
||||
else:
|
||||
linked_button.texture_normal = load("res://Assets/Graphics/%s Themes/Layers/Unlinked_Layer.png" % Global.theme_type)
|
||||
linked_button.texture_hover = load("res://Assets/Graphics/%s Themes/Layers/Unlinked_Layer_Hover.png" % Global.theme_type)
|
||||
|
||||
|
||||
func _input(event : InputEvent) -> void:
|
||||
if (event.is_action_released("ui_accept") or event.is_action_released("ui_cancel")) and line_edit.visible and event.scancode != KEY_SPACE:
|
||||
save_layer_name(line_edit.text)
|
||||
|
||||
|
||||
func _on_LayerContainer_pressed() -> void:
|
||||
pressed = !pressed
|
||||
label.visible = false
|
||||
line_edit.visible = true
|
||||
line_edit.editable = true
|
||||
line_edit.grab_focus()
|
||||
|
||||
|
||||
func _on_LineEdit_focus_exited() -> void:
|
||||
save_layer_name(line_edit.text)
|
||||
|
||||
|
||||
func save_layer_name(new_name : String) -> void:
|
||||
label.visible = true
|
||||
line_edit.visible = false
|
||||
line_edit.editable = false
|
||||
label.text = new_name
|
||||
Global.layers_changed_skip = true
|
||||
Global.layers[i][0] = new_name
|
||||
|
||||
|
||||
func _on_VisibilityButton_pressed() -> void:
|
||||
Global.layers[i][1] = !Global.layers[i][1]
|
||||
Global.canvas.update()
|
||||
|
||||
|
||||
func _on_LockButton_pressed() -> void:
|
||||
Global.layers[i][2] = !Global.layers[i][2]
|
||||
|
||||
|
||||
func _on_LinkButton_pressed() -> void:
|
||||
Global.layers[i][4] = !Global.layers[i][4]
|
||||
if Global.layers[i][4] && !Global.layers[i][5]:
|
||||
Global.layers[i][5].append(Global.canvas)
|
||||
Global.layers[i][3].get_child(Global.current_frame)._ready()
|
119
src/LayerButton.tscn
Normal file
119
src/LayerButton.tscn
Normal file
|
@ -0,0 +1,119 @@
|
|||
[gd_scene load_steps=8 format=2]
|
||||
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Layers/Unlinked_Layer.png" type="Texture" id=1]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Layers/Unlock_Hover.png" type="Texture" id=2]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Layers/Unlock.png" type="Texture" id=3]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Layers/Unlinked_Layer_Hover.png" type="Texture" id=4]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Layers/Layer_Visible.png" type="Texture" id=5]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Layers/Layer_Visible_Hover.png" type="Texture" id=6]
|
||||
[ext_resource path="res://src/LayerButton.gd" type="Script" id=7]
|
||||
|
||||
[node name="LayerContainer" type="Button"]
|
||||
margin_right = 210.0
|
||||
margin_bottom = 36.0
|
||||
rect_min_size = Vector2( 212, 36 )
|
||||
size_flags_horizontal = 0
|
||||
toggle_mode = true
|
||||
action_mode = 0
|
||||
script = ExtResource( 7 )
|
||||
__meta__ = {
|
||||
"_edit_horizontal_guides_": [ ],
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="."]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
size_flags_horizontal = 0
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="LayerButtons" type="HBoxContainer" parent="HBoxContainer"]
|
||||
margin_right = 104.0
|
||||
margin_bottom = 36.0
|
||||
|
||||
[node name="VisibilityButton" type="TextureButton" parent="HBoxContainer/LayerButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_top = 2.0
|
||||
margin_right = 32.0
|
||||
margin_bottom = 34.0
|
||||
hint_tooltip = "Toggle layer's visibility"
|
||||
focus_mode = 0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 0
|
||||
size_flags_vertical = 4
|
||||
texture_normal = ExtResource( 5 )
|
||||
texture_hover = ExtResource( 6 )
|
||||
|
||||
[node name="LockButton" type="TextureButton" parent="HBoxContainer/LayerButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_left = 36.0
|
||||
margin_top = 2.0
|
||||
margin_right = 68.0
|
||||
margin_bottom = 34.0
|
||||
hint_tooltip = "Lock/unlock layer"
|
||||
focus_mode = 0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 0
|
||||
size_flags_vertical = 4
|
||||
texture_normal = ExtResource( 3 )
|
||||
texture_hover = ExtResource( 2 )
|
||||
|
||||
[node name="LinkButton" type="TextureButton" parent="HBoxContainer/LayerButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_left = 72.0
|
||||
margin_top = 2.0
|
||||
margin_right = 104.0
|
||||
margin_bottom = 34.0
|
||||
hint_tooltip = "Enable/disable cel linking
|
||||
|
||||
Linked cels are being shared across multiple frames"
|
||||
focus_mode = 0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 0
|
||||
size_flags_vertical = 4
|
||||
texture_normal = ExtResource( 1 )
|
||||
texture_hover = ExtResource( 4 )
|
||||
|
||||
[node name="LayerName" type="HBoxContainer" parent="HBoxContainer"]
|
||||
margin_left = 108.0
|
||||
margin_right = 212.0
|
||||
margin_bottom = 36.0
|
||||
rect_min_size = Vector2( 104, 0 )
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 0
|
||||
alignment = 1
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Label" type="Label" parent="HBoxContainer/LayerName"]
|
||||
margin_top = 11.0
|
||||
margin_right = 104.0
|
||||
margin_bottom = 25.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Layer 0"
|
||||
align = 1
|
||||
clip_text = true
|
||||
|
||||
[node name="LineEdit" type="LineEdit" parent="HBoxContainer/LayerName"]
|
||||
visible = false
|
||||
margin_left = 86.0
|
||||
margin_top = 5.0
|
||||
margin_right = 166.0
|
||||
margin_bottom = 37.0
|
||||
rect_min_size = Vector2( 80, 32 )
|
||||
size_flags_vertical = 4
|
||||
text = "Layer 0"
|
||||
editable = false
|
||||
caret_blink = true
|
||||
caret_blink_speed = 0.5
|
||||
[connection signal="pressed" from="." to="." method="_on_LayerContainer_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/LineEdit" to="." method="_on_LineEdit_focus_exited"]
|
869
src/Main.gd
Normal file
869
src/Main.gd
Normal file
|
@ -0,0 +1,869 @@
|
|||
extends Control
|
||||
|
||||
var opensprite_file_selected := false
|
||||
var file_menu : PopupMenu
|
||||
var view_menu : PopupMenu
|
||||
var tools := []
|
||||
var redone := false
|
||||
var unsaved_canvas_state := 0
|
||||
var is_quitting_on_save := false
|
||||
var previous_left_color := Color.black
|
||||
var previous_right_color := Color.white
|
||||
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready() -> void:
|
||||
get_tree().set_auto_accept_quit(false)
|
||||
# Set a minimum window size to prevent UI elements from collapsing on each other.
|
||||
# This property is only available in 3.2alpha or later, so use `set()` to fail gracefully if it doesn't exist.
|
||||
OS.set("min_window_size", Vector2(1024, 576))
|
||||
|
||||
# `TranslationServer.get_loaded_locales()` was added in 3.2beta and in 3.1.2
|
||||
# The `has_method()` check and the `else` branch can be removed once 3.2 is released.
|
||||
if TranslationServer.has_method("get_loaded_locales"):
|
||||
Global.loaded_locales = TranslationServer.get_loaded_locales()
|
||||
else:
|
||||
# Hardcoded list of locales
|
||||
Global.loaded_locales = ["de_DE", "el_GR", "en_US", "eo_UY", "es_ES", "fr_FR", "it_IT", "lv_LV", "pl_PL", "pt_BR", "ru_RU", "zh_CN","zh_TW"]
|
||||
|
||||
# Make sure locales are always sorted, in the same order
|
||||
Global.loaded_locales.sort()
|
||||
|
||||
# Restore the window position/size if values are present in the configuration cache
|
||||
if Global.config_cache.has_section_key("window", "screen"):
|
||||
OS.current_screen = Global.config_cache.get_value("window", "screen")
|
||||
if Global.config_cache.has_section_key("window", "maximized"):
|
||||
OS.window_maximized = Global.config_cache.get_value("window", "maximized")
|
||||
|
||||
if !OS.window_maximized:
|
||||
if Global.config_cache.has_section_key("window", "position"):
|
||||
OS.window_position = Global.config_cache.get_value("window", "position")
|
||||
if Global.config_cache.has_section_key("window", "size"):
|
||||
OS.window_size = Global.config_cache.get_value("window", "size")
|
||||
|
||||
var file_menu_items := {
|
||||
"New..." : InputMap.get_action_list("new_file")[0].get_scancode_with_modifiers(),
|
||||
"Open..." : InputMap.get_action_list("open_file")[0].get_scancode_with_modifiers(),
|
||||
'Open last project...' : 0,
|
||||
"Save..." : InputMap.get_action_list("save_file")[0].get_scancode_with_modifiers(),
|
||||
"Save as..." : InputMap.get_action_list("save_file_as")[0].get_scancode_with_modifiers(),
|
||||
"Import..." : InputMap.get_action_list("import_file")[0].get_scancode_with_modifiers(),
|
||||
"Export..." : InputMap.get_action_list("export_file")[0].get_scancode_with_modifiers(),
|
||||
"Export as..." : InputMap.get_action_list("export_file_as")[0].get_scancode_with_modifiers(),
|
||||
"Quit" : InputMap.get_action_list("quit")[0].get_scancode_with_modifiers(),
|
||||
}
|
||||
var edit_menu_items := {
|
||||
"Undo" : InputMap.get_action_list("undo")[0].get_scancode_with_modifiers(),
|
||||
"Redo" : InputMap.get_action_list("redo")[0].get_scancode_with_modifiers(),
|
||||
"Clear Selection" : 0,
|
||||
"Preferences" : 0
|
||||
}
|
||||
var view_menu_items := {
|
||||
"Tile Mode" : InputMap.get_action_list("tile_mode")[0].get_scancode_with_modifiers(),
|
||||
"Show Grid" : InputMap.get_action_list("show_grid")[0].get_scancode_with_modifiers(),
|
||||
"Show Rulers" : InputMap.get_action_list("show_rulers")[0].get_scancode_with_modifiers(),
|
||||
"Show Guides" : InputMap.get_action_list("show_guides")[0].get_scancode_with_modifiers(),
|
||||
"Show Animation Timeline" : 0
|
||||
}
|
||||
var image_menu_items := {
|
||||
"Scale Image" : 0,
|
||||
"Crop Image" : 0,
|
||||
"Flip Horizontal" : InputMap.get_action_list("image_flip_horizontal")[0].get_scancode_with_modifiers(),
|
||||
"Flip Vertical" : InputMap.get_action_list("image_flip_vertical")[0].get_scancode_with_modifiers(),
|
||||
"Rotate Image" : 0,
|
||||
"Invert colors" : 0,
|
||||
"Desaturation" : 0,
|
||||
"Outline" : 0,
|
||||
"Adjust Hue/Saturation/Value" : 0
|
||||
}
|
||||
var help_menu_items := {
|
||||
"View Splash Screen" : 0,
|
||||
"Issue Tracker" : 0,
|
||||
"Changelog" : 0,
|
||||
"About Pixelorama" : 0
|
||||
}
|
||||
|
||||
# Load language
|
||||
if Global.config_cache.has_section_key("preferences", "locale"):
|
||||
var saved_locale : String = Global.config_cache.get_value("preferences", "locale")
|
||||
TranslationServer.set_locale(saved_locale)
|
||||
|
||||
# Set the language option menu's default selected option to the loaded locale
|
||||
var locale_index: int = Global.loaded_locales.find(saved_locale)
|
||||
$PreferencesDialog.languages.get_child(0).pressed = false # Unset System Language option in preferences
|
||||
$PreferencesDialog.languages.get_child(locale_index + 1).pressed = true
|
||||
else: # If the user doesn't have a language preference, set it to their OS' locale
|
||||
TranslationServer.set_locale(OS.get_locale())
|
||||
|
||||
if "zh" in TranslationServer.get_locale():
|
||||
theme.default_font = preload("res://Assets/Fonts/CJK/NotoSansCJKtc-Regular.tres")
|
||||
else:
|
||||
theme.default_font = preload("res://Assets/Fonts/Roboto-Regular.tres")
|
||||
|
||||
|
||||
file_menu = Global.file_menu.get_popup()
|
||||
var edit_menu : PopupMenu = Global.edit_menu.get_popup()
|
||||
view_menu = Global.view_menu.get_popup()
|
||||
var image_menu : PopupMenu = Global.image_menu.get_popup()
|
||||
var help_menu : PopupMenu = Global.help_menu.get_popup()
|
||||
|
||||
var i = 0
|
||||
for item in file_menu_items.keys():
|
||||
file_menu.add_item(item, i, file_menu_items[item])
|
||||
i += 1
|
||||
i = 0
|
||||
for item in edit_menu_items.keys():
|
||||
edit_menu.add_item(item, i, edit_menu_items[item])
|
||||
i += 1
|
||||
i = 0
|
||||
for item in view_menu_items.keys():
|
||||
view_menu.add_check_item(item, i, view_menu_items[item])
|
||||
i += 1
|
||||
view_menu.set_item_checked(2, true) # Show Rulers
|
||||
view_menu.set_item_checked(3, true) # Show Guides
|
||||
view_menu.set_item_checked(4, true) # Show Animation Timeline
|
||||
view_menu.hide_on_checkable_item_selection = false
|
||||
i = 0
|
||||
for item in image_menu_items.keys():
|
||||
image_menu.add_item(item, i, image_menu_items[item])
|
||||
if i == 4:
|
||||
image_menu.add_separator()
|
||||
i += 1
|
||||
i = 0
|
||||
for item in help_menu_items.keys():
|
||||
help_menu.add_item(item, i, help_menu_items[item])
|
||||
i += 1
|
||||
|
||||
file_menu.connect("id_pressed", self, "file_menu_id_pressed")
|
||||
edit_menu.connect("id_pressed", self, "edit_menu_id_pressed")
|
||||
view_menu.connect("id_pressed", self, "view_menu_id_pressed")
|
||||
image_menu.connect("id_pressed", self, "image_menu_id_pressed")
|
||||
help_menu.connect("id_pressed", self, "help_menu_id_pressed")
|
||||
|
||||
var root = get_tree().get_root()
|
||||
# Node, left mouse shortcut, right mouse shortcut
|
||||
tools.append([Global.find_node_by_name(root, "Pencil"), "left_pencil_tool", "right_pencil_tool"])
|
||||
tools.append([Global.find_node_by_name(root, "Eraser"), "left_eraser_tool", "right_eraser_tool"])
|
||||
tools.append([Global.find_node_by_name(root, "Bucket"), "left_fill_tool", "right_fill_tool"])
|
||||
tools.append([Global.find_node_by_name(root, "LightenDarken"), "left_lightdark_tool", "right_lightdark_tool"])
|
||||
tools.append([Global.find_node_by_name(root, "RectSelect"), "left_rectangle_select_tool", "right_rectangle_select_tool"])
|
||||
tools.append([Global.find_node_by_name(root, "ColorPicker"), "left_colorpicker_tool", "right_colorpicker_tool"])
|
||||
tools.append([Global.find_node_by_name(root, "Zoom"), "left_zoom_tool", "right_zoom_tool"])
|
||||
|
||||
for t in tools:
|
||||
t[0].connect("pressed", self, "_on_Tool_pressed", [t[0]])
|
||||
|
||||
Global.update_hint_tooltips()
|
||||
|
||||
# Checks to see if it's 3.1.x
|
||||
if Engine.get_version_info().major == 3 and Engine.get_version_info().minor < 2:
|
||||
Global.left_color_picker.get_picker().move_child(Global.left_color_picker.get_picker().get_child(0), 1)
|
||||
Global.right_color_picker.get_picker().move_child(Global.right_color_picker.get_picker().get_child(0), 1)
|
||||
|
||||
if OS.get_cmdline_args():
|
||||
for arg in OS.get_cmdline_args():
|
||||
if arg.get_extension().to_lower() == "pxo":
|
||||
_on_OpenSprite_file_selected(arg)
|
||||
else:
|
||||
$ImportSprites._on_ImportSprites_files_selected([arg])
|
||||
|
||||
Global.window_title = "(" + tr("untitled") + ") - Pixelorama"
|
||||
|
||||
Global.layers[0][0] = tr("Layer") + " 0"
|
||||
Global.layers_container.get_child(0).label.text = Global.layers[0][0]
|
||||
Global.layers_container.get_child(0).line_edit.text = Global.layers[0][0]
|
||||
|
||||
Import.import_brushes(Global.directory_module.get_brushes_search_path_in_order())
|
||||
Import.import_patterns(Global.directory_module.get_patterns_search_path_in_order())
|
||||
|
||||
Global.left_color_picker.get_picker().presets_visible = false
|
||||
Global.right_color_picker.get_picker().presets_visible = false
|
||||
$QuitAndSaveDialog.add_button("Save & Exit", false, "Save")
|
||||
$QuitAndSaveDialog.get_ok().text = "Exit without saving"
|
||||
|
||||
|
||||
if not Global.config_cache.has_section_key("preferences", "startup"):
|
||||
Global.config_cache.set_value("preferences", "startup", true)
|
||||
if Global.config_cache.get_value("preferences", "startup"):
|
||||
# Wait for the window to adjust itself, so the popup is correctly centered
|
||||
yield(get_tree().create_timer(0.01), "timeout")
|
||||
$SplashDialog.popup_centered() # Splash screen
|
||||
else:
|
||||
Global.can_draw = true
|
||||
|
||||
if not Global.config_cache.has_section_key("preferences", "open_last_project"):
|
||||
Global.config_cache.set_value("preferences", "open_last_project", true)
|
||||
if Global.config_cache.get_value("preferences", "open_last_project"):
|
||||
Global.open_last_project = Global.config_cache.get_value("preferences", "open_last_project")
|
||||
|
||||
# If backup file exists then Pixelorama was not closed properly (probably crashed) - reopen backup
|
||||
$BackupConfirmation.get_cancel().text = tr("Delete")
|
||||
if Global.config_cache.has_section("backups"):
|
||||
var project_paths = Global.config_cache.get_section_keys("backups")
|
||||
if project_paths.size() > 0:
|
||||
# Get backup path
|
||||
var backup_path = Global.config_cache.get_value("backups", project_paths[0])
|
||||
# Temporatily stop autosave until user confirms backup
|
||||
OpenSave.autosave_timer.stop()
|
||||
Global.can_draw = false
|
||||
# For it's only possible to reload the first found backup
|
||||
$BackupConfirmation.dialog_text = $BackupConfirmation.dialog_text % project_paths[0]
|
||||
$BackupConfirmation.connect("confirmed", self, "_on_BackupConfirmation_confirmed", [project_paths[0], backup_path])
|
||||
$BackupConfirmation.get_cancel().connect("pressed", self, "_on_BackupConfirmation_delete", [project_paths[0], backup_path])
|
||||
$BackupConfirmation.popup_centered()
|
||||
else:
|
||||
if Global.open_last_project:
|
||||
load_last_project()
|
||||
else:
|
||||
if Global.open_last_project:
|
||||
load_last_project()
|
||||
|
||||
|
||||
func _input(event : InputEvent) -> void:
|
||||
Global.left_cursor.position = get_global_mouse_position() + Vector2(-32, 32)
|
||||
Global.left_cursor.texture = Global.left_cursor_tool_texture
|
||||
Global.right_cursor.position = get_global_mouse_position() + Vector2(32, 32)
|
||||
Global.right_cursor.texture = Global.right_cursor_tool_texture
|
||||
|
||||
if event is InputEventKey and (event.scancode == KEY_ENTER or event.scancode == KEY_KP_ENTER):
|
||||
if get_focus_owner() is LineEdit:
|
||||
get_focus_owner().release_focus()
|
||||
|
||||
if event.is_action_pressed("toggle_fullscreen"):
|
||||
OS.window_fullscreen = !OS.window_fullscreen
|
||||
|
||||
if event.is_action_pressed("redo_secondary"): # Shift + Ctrl + Z
|
||||
redone = true
|
||||
Global.undo_redo.redo()
|
||||
redone = false
|
||||
|
||||
if Global.has_focus:
|
||||
if event.is_action_pressed("undo") or event.is_action_pressed("redo") or event.is_action_pressed("redo_secondary"):
|
||||
return
|
||||
for t in tools: # Handle tool shortcuts
|
||||
if event.is_action_pressed(t[2]): # Shortcut for right button (with Alt)
|
||||
_on_Tool_pressed(t[0], false, false)
|
||||
elif event.is_action_pressed(t[1]): # Shortcut for left button
|
||||
_on_Tool_pressed(t[0], false, true)
|
||||
|
||||
|
||||
func _notification(what : int) -> void:
|
||||
if what == MainLoop.NOTIFICATION_WM_QUIT_REQUEST: # Handle exit
|
||||
show_quit_dialog()
|
||||
|
||||
|
||||
func file_menu_id_pressed(id : int) -> void:
|
||||
match id:
|
||||
0: # New
|
||||
if Global.project_has_changed:
|
||||
unsaved_canvas_state = id
|
||||
$UnsavedCanvasDialog.popup_centered()
|
||||
else:
|
||||
$CreateNewImage.popup_centered()
|
||||
Global.can_draw = false
|
||||
1: # Open
|
||||
$OpenSprite.popup_centered()
|
||||
Global.can_draw = false
|
||||
opensprite_file_selected = false
|
||||
2: # Open last project
|
||||
# Check if last project path is set and if yes then open
|
||||
if Global.config_cache.has_section_key("preferences", "last_project_path"):
|
||||
if Global.project_has_changed:
|
||||
unsaved_canvas_state = id
|
||||
$UnsavedCanvasDialog.popup_centered()
|
||||
else:
|
||||
load_last_project()
|
||||
else: # if not then warn user that he didn't edit any project yet
|
||||
$NoProjectEditedOrCreatedAlertDialog.popup_centered()
|
||||
3: # Save
|
||||
is_quitting_on_save = false
|
||||
if OpenSave.current_save_path == "":
|
||||
$SaveSprite.popup_centered()
|
||||
Global.can_draw = false
|
||||
else:
|
||||
_on_SaveSprite_file_selected(OpenSave.current_save_path)
|
||||
4: # Save as
|
||||
is_quitting_on_save = false
|
||||
$SaveSprite.popup_centered()
|
||||
Global.can_draw = false
|
||||
5: # Import
|
||||
$ImportSprites.popup_centered()
|
||||
Global.can_draw = false
|
||||
opensprite_file_selected = false
|
||||
6: # Export
|
||||
if $ExportDialog.was_exported == false:
|
||||
$ExportDialog.popup_centered()
|
||||
Global.can_draw = false
|
||||
else:
|
||||
$ExportDialog.external_export()
|
||||
7: # Export as
|
||||
$ExportDialog.popup_centered()
|
||||
Global.can_draw = false
|
||||
8: # Quit
|
||||
show_quit_dialog()
|
||||
|
||||
|
||||
func edit_menu_id_pressed(id : int) -> void:
|
||||
match id:
|
||||
0: # Undo
|
||||
Global.undo_redo.undo()
|
||||
1: # Redo
|
||||
redone = true
|
||||
Global.undo_redo.redo()
|
||||
redone = false
|
||||
2: # Clear selection
|
||||
Global.canvas.handle_undo("Rectangle Select")
|
||||
Global.selection_rectangle.polygon[0] = Vector2.ZERO
|
||||
Global.selection_rectangle.polygon[1] = Vector2.ZERO
|
||||
Global.selection_rectangle.polygon[2] = Vector2.ZERO
|
||||
Global.selection_rectangle.polygon[3] = Vector2.ZERO
|
||||
Global.selected_pixels.clear()
|
||||
Global.canvas.handle_redo("Rectangle Select")
|
||||
3: # Preferences
|
||||
$PreferencesDialog.popup_centered(Vector2(400, 280))
|
||||
Global.can_draw = false
|
||||
|
||||
|
||||
func view_menu_id_pressed(id : int) -> void:
|
||||
match id:
|
||||
0: # Tile mode
|
||||
Global.tile_mode = !Global.tile_mode
|
||||
view_menu.set_item_checked(0, Global.tile_mode)
|
||||
1: # Show grid
|
||||
Global.draw_grid = !Global.draw_grid
|
||||
view_menu.set_item_checked(1, Global.draw_grid)
|
||||
2: # Show rulers
|
||||
Global.show_rulers = !Global.show_rulers
|
||||
view_menu.set_item_checked(2, Global.show_rulers)
|
||||
Global.horizontal_ruler.visible = Global.show_rulers
|
||||
Global.vertical_ruler.visible = Global.show_rulers
|
||||
3: # Show guides
|
||||
Global.show_guides = !Global.show_guides
|
||||
view_menu.set_item_checked(3, Global.show_guides)
|
||||
for canvas in Global.canvases:
|
||||
for guide in canvas.get_children():
|
||||
if guide is Guide:
|
||||
guide.visible = Global.show_guides
|
||||
4: # Show animation timeline
|
||||
Global.show_animation_timeline = !Global.show_animation_timeline
|
||||
view_menu.set_item_checked(4, Global.show_animation_timeline)
|
||||
Global.animation_timeline.visible = Global.show_animation_timeline
|
||||
|
||||
Global.canvas.update()
|
||||
|
||||
|
||||
func image_menu_id_pressed(id : int) -> void:
|
||||
if Global.layers[Global.current_layer][2]: # No changes if the layer is locked
|
||||
return
|
||||
match id:
|
||||
0: # Scale Image
|
||||
$ScaleImage.popup_centered()
|
||||
Global.can_draw = false
|
||||
1: # Crop Image
|
||||
# Use first layer as a starting rectangle
|
||||
var used_rect : Rect2 = Global.canvas.layers[0][0].get_used_rect()
|
||||
# However, if first layer is empty, loop through all layers until we find one that isn't
|
||||
var i := 0
|
||||
while(i < Global.canvas.layers.size() - 1 and Global.canvas.layers[i][0].get_used_rect() == Rect2(0, 0, 0, 0)):
|
||||
i += 1
|
||||
used_rect = Global.canvas.layers[i][0].get_used_rect()
|
||||
|
||||
# Merge all layers with content
|
||||
for j in range(Global.canvas.layers.size() - 1, i, -1):
|
||||
if Global.canvas.layers[j][0].get_used_rect() != Rect2(0, 0, 0, 0):
|
||||
used_rect = used_rect.merge(Global.canvas.layers[j][0].get_used_rect())
|
||||
|
||||
# If no layer has any content, just return
|
||||
if used_rect == Rect2(0, 0, 0, 0):
|
||||
return
|
||||
|
||||
var width := used_rect.size.x
|
||||
var height := used_rect.size.y
|
||||
Global.undos += 1
|
||||
Global.undo_redo.create_action("Scale")
|
||||
Global.undo_redo.add_do_property(Global.canvas, "size", Vector2(width, height).floor())
|
||||
# Loop through all the layers to crop them
|
||||
for j in range(Global.canvas.layers.size() - 1, -1, -1):
|
||||
var sprite : Image = Global.canvas.layers[j][0].get_rect(used_rect)
|
||||
Global.undo_redo.add_do_property(Global.canvas.layers[j][0], "data", sprite.data)
|
||||
Global.undo_redo.add_undo_property(Global.canvas.layers[j][0], "data", Global.canvas.layers[j][0].data)
|
||||
|
||||
Global.undo_redo.add_undo_property(Global.canvas, "size", Global.canvas.size)
|
||||
Global.undo_redo.add_undo_method(Global, "undo", [Global.canvas])
|
||||
Global.undo_redo.add_do_method(Global, "redo", [Global.canvas])
|
||||
Global.undo_redo.commit_action()
|
||||
2: # Flip Horizontal
|
||||
var canvas : Canvas = Global.canvas
|
||||
canvas.handle_undo("Draw")
|
||||
canvas.layers[Global.current_layer][0].unlock()
|
||||
canvas.layers[Global.current_layer][0].flip_x()
|
||||
canvas.layers[Global.current_layer][0].lock()
|
||||
canvas.handle_redo("Draw")
|
||||
3: # Flip Vertical
|
||||
var canvas : Canvas = Global.canvas
|
||||
canvas.handle_undo("Draw")
|
||||
canvas.layers[Global.current_layer][0].unlock()
|
||||
canvas.layers[Global.current_layer][0].flip_y()
|
||||
canvas.layers[Global.current_layer][0].lock()
|
||||
canvas.handle_redo("Draw")
|
||||
4: # Rotate
|
||||
var image : Image = Global.canvas.layers[Global.current_layer][0]
|
||||
$RotateImage.set_sprite(image)
|
||||
$RotateImage.popup_centered()
|
||||
Global.can_draw = false
|
||||
5: # Invert Colors
|
||||
var image : Image = Global.canvas.layers[Global.current_layer][0]
|
||||
Global.canvas.handle_undo("Draw")
|
||||
for xx in image.get_size().x:
|
||||
for yy in image.get_size().y:
|
||||
var px_color = image.get_pixel(xx, yy).inverted()
|
||||
if px_color.a == 0:
|
||||
continue
|
||||
image.set_pixel(xx, yy, px_color)
|
||||
Global.canvas.handle_redo("Draw")
|
||||
6: # Desaturation
|
||||
var image : Image = Global.canvas.layers[Global.current_layer][0]
|
||||
Global.canvas.handle_undo("Draw")
|
||||
for xx in image.get_size().x:
|
||||
for yy in image.get_size().y:
|
||||
var px_color = image.get_pixel(xx, yy)
|
||||
if px_color.a == 0:
|
||||
continue
|
||||
var gray = image.get_pixel(xx, yy).v
|
||||
px_color = Color(gray, gray, gray, px_color.a)
|
||||
image.set_pixel(xx, yy, px_color)
|
||||
Global.canvas.handle_redo("Draw")
|
||||
7: # Outline
|
||||
$OutlineDialog.popup_centered()
|
||||
Global.can_draw = false
|
||||
8: # HSV
|
||||
$HSVDialog.popup_centered()
|
||||
Global.can_draw = false
|
||||
|
||||
|
||||
func help_menu_id_pressed(id : int) -> void:
|
||||
match id:
|
||||
0: # Splash Screen
|
||||
$SplashDialog.popup_centered()
|
||||
Global.can_draw = false
|
||||
1: # Issue Tracker
|
||||
OS.shell_open("https://github.com/Orama-Interactive/Pixelorama/issues")
|
||||
2: # Changelog
|
||||
OS.shell_open("https://github.com/Orama-Interactive/Pixelorama/blob/master/Changelog.md#v062---17-02-2020")
|
||||
3: # About Pixelorama
|
||||
$AboutDialog.popup_centered()
|
||||
Global.can_draw = false
|
||||
|
||||
|
||||
func load_last_project() -> void:
|
||||
# Check if any project was saved or opened last time
|
||||
if Global.config_cache.has_section_key("preferences", "last_project_path"):
|
||||
# Check if file still exists on disk
|
||||
var file_path = Global.config_cache.get_value("preferences", "last_project_path")
|
||||
var file_check := File.new()
|
||||
if file_check.file_exists(file_path): # If yes then load the file
|
||||
_on_OpenSprite_file_selected(file_path)
|
||||
else:
|
||||
# If file doesn't exist on disk then warn user about this
|
||||
$OpenLastProjectAlertDialog.popup_centered()
|
||||
|
||||
|
||||
func _on_UnsavedCanvasDialog_confirmed() -> void:
|
||||
if unsaved_canvas_state == 0: # New image
|
||||
$CreateNewImage.popup_centered()
|
||||
elif unsaved_canvas_state == 2: # Open last project
|
||||
load_last_project()
|
||||
|
||||
|
||||
func _on_OpenSprite_file_selected(path : String) -> void:
|
||||
OpenSave.open_pxo_file(path)
|
||||
|
||||
$SaveSprite.current_path = path
|
||||
# Set last opened project path and save
|
||||
Global.config_cache.set_value("preferences", "last_project_path", path)
|
||||
Global.config_cache.save("user://cache.ini")
|
||||
$ExportDialog.file_name = path.get_file().trim_suffix(".pxo")
|
||||
$ExportDialog.directory_path = path.get_base_dir()
|
||||
$ExportDialog.was_exported = false
|
||||
file_menu.set_item_text(3, tr("Save") + " %s" % path.get_file())
|
||||
file_menu.set_item_text(6, tr("Export"))
|
||||
|
||||
|
||||
func _on_SaveSprite_file_selected(path : String) -> void:
|
||||
OpenSave.save_pxo_file(path, false)
|
||||
|
||||
# Set last opened project path and save
|
||||
Global.config_cache.set_value("preferences", "last_project_path", path)
|
||||
Global.config_cache.save("user://cache.ini")
|
||||
$ExportDialog.file_name = path.get_file().trim_suffix(".pxo")
|
||||
$ExportDialog.directory_path = path.get_base_dir()
|
||||
$ExportDialog.was_exported = false
|
||||
file_menu.set_item_text(3, tr("Save") + " %s" % path.get_file())
|
||||
|
||||
if is_quitting_on_save:
|
||||
_on_QuitDialog_confirmed()
|
||||
|
||||
|
||||
func _on_ImportSprites_popup_hide() -> void:
|
||||
if !opensprite_file_selected:
|
||||
Global.can_draw = true
|
||||
|
||||
|
||||
func _on_ViewportContainer_mouse_entered() -> void:
|
||||
Global.has_focus = true
|
||||
|
||||
|
||||
func _on_ViewportContainer_mouse_exited() -> void:
|
||||
Global.has_focus = false
|
||||
|
||||
|
||||
func _can_draw_true() -> void:
|
||||
Global.can_draw = true
|
||||
|
||||
|
||||
func _can_draw_false() -> void:
|
||||
Global.can_draw = false
|
||||
|
||||
|
||||
func _on_Tool_pressed(tool_pressed : BaseButton, mouse_press := true, key_for_left := true) -> void:
|
||||
var current_action := tool_pressed.name
|
||||
if (mouse_press and Input.is_action_just_released("left_mouse")) or (!mouse_press and key_for_left):
|
||||
Global.current_left_tool = current_action
|
||||
|
||||
# Start from 1, so the label won't get invisible
|
||||
for i in range(1, Global.left_tool_options_container.get_child_count()):
|
||||
Global.left_tool_options_container.get_child(i).visible = false
|
||||
|
||||
Global.left_tool_options_container.get_node("EmptySpacer").visible = true
|
||||
|
||||
# Tool options visible depending on the selected tool
|
||||
if current_action == "Pencil":
|
||||
Global.left_brush_type_container.visible = true
|
||||
Global.left_brush_size_slider.visible = true
|
||||
Global.left_pixel_perfect_container.visible = true
|
||||
Global.left_mirror_container.visible = true
|
||||
if Global.current_left_brush_type == Global.Brush_Types.FILE or Global.current_left_brush_type == Global.Brush_Types.CUSTOM or Global.current_left_brush_type == Global.Brush_Types.RANDOM_FILE:
|
||||
Global.left_color_interpolation_container.visible = true
|
||||
elif current_action == "Eraser":
|
||||
Global.left_brush_type_container.visible = true
|
||||
Global.left_brush_size_slider.visible = true
|
||||
Global.left_pixel_perfect_container.visible = true
|
||||
Global.left_mirror_container.visible = true
|
||||
elif current_action == "Bucket":
|
||||
Global.left_fill_area_container.visible = true
|
||||
Global.left_mirror_container.visible = true
|
||||
elif current_action == "LightenDarken":
|
||||
Global.left_brush_type_container.visible = true
|
||||
Global.left_brush_size_slider.visible = true
|
||||
Global.left_pixel_perfect_container.visible = true
|
||||
Global.left_ld_container.visible = true
|
||||
Global.left_mirror_container.visible = true
|
||||
elif current_action == "ColorPicker":
|
||||
Global.left_colorpicker_container.visible = true
|
||||
elif current_action == "Zoom":
|
||||
Global.left_zoom_container.visible = true
|
||||
|
||||
elif (mouse_press and Input.is_action_just_released("right_mouse")) or (!mouse_press and !key_for_left):
|
||||
Global.current_right_tool = current_action
|
||||
# Start from 1, so the label won't get invisible
|
||||
for i in range(1, Global.right_tool_options_container.get_child_count()):
|
||||
Global.right_tool_options_container.get_child(i).visible = false
|
||||
|
||||
Global.right_tool_options_container.get_node("EmptySpacer").visible = true
|
||||
|
||||
# Tool options visible depending on the selected tool
|
||||
if current_action == "Pencil":
|
||||
Global.right_brush_type_container.visible = true
|
||||
Global.right_brush_size_slider.visible = true
|
||||
Global.right_pixel_perfect_container.visible = true
|
||||
Global.right_mirror_container.visible = true
|
||||
if Global.current_right_brush_type == Global.Brush_Types.FILE or Global.current_right_brush_type == Global.Brush_Types.CUSTOM or Global.current_right_brush_type == Global.Brush_Types.RANDOM_FILE:
|
||||
Global.right_color_interpolation_container.visible = true
|
||||
elif current_action == "Eraser":
|
||||
Global.right_brush_type_container.visible = true
|
||||
Global.right_brush_size_slider.visible = true
|
||||
Global.right_pixel_perfect_container.visible = true
|
||||
Global.right_mirror_container.visible = true
|
||||
elif current_action == "Bucket":
|
||||
Global.right_fill_area_container.visible = true
|
||||
Global.right_mirror_container.visible = true
|
||||
elif current_action == "LightenDarken":
|
||||
Global.right_brush_type_container.visible = true
|
||||
Global.right_brush_size_slider.visible = true
|
||||
Global.right_pixel_perfect_container.visible = true
|
||||
Global.right_ld_container.visible = true
|
||||
Global.right_mirror_container.visible = true
|
||||
elif current_action == "ColorPicker":
|
||||
Global.right_colorpicker_container.visible = true
|
||||
elif current_action == "Zoom":
|
||||
Global.right_zoom_container.visible = true
|
||||
|
||||
for t in tools:
|
||||
var tool_name : String = t[0].name
|
||||
if tool_name == Global.current_left_tool and tool_name == Global.current_right_tool:
|
||||
t[0].texture_normal = load("res://Assets/Graphics/%s Themes/Tools/%s_l_r.png" % [Global.theme_type, tool_name])
|
||||
elif tool_name == Global.current_left_tool:
|
||||
t[0].texture_normal = load("res://Assets/Graphics/%s Themes/Tools/%s_l.png" % [Global.theme_type, tool_name])
|
||||
elif tool_name == Global.current_right_tool:
|
||||
t[0].texture_normal = load("res://Assets/Graphics/%s Themes/Tools/%s_r.png" % [Global.theme_type, tool_name])
|
||||
else:
|
||||
t[0].texture_normal = load("res://Assets/Graphics/%s Themes/Tools/%s.png" % [Global.theme_type, tool_name])
|
||||
|
||||
Global.left_cursor_tool_texture.create_from_image(load("res://Assets/Graphics/Tool Cursors/%s_Cursor.png" % Global.current_left_tool), 0)
|
||||
Global.right_cursor_tool_texture.create_from_image(load("res://Assets/Graphics/Tool Cursors/%s_Cursor.png" % Global.current_right_tool), 0)
|
||||
|
||||
|
||||
func _on_LeftBrushTypeButton_pressed() -> void:
|
||||
Global.brushes_popup.popup(Rect2(Global.left_brush_type_button.rect_global_position, Vector2(226, 72)))
|
||||
Global.brush_type_window_position = "left"
|
||||
|
||||
|
||||
func _on_RightBrushTypeButton_pressed() -> void:
|
||||
Global.brushes_popup.popup(Rect2(Global.right_brush_type_button.rect_global_position, Vector2(226, 72)))
|
||||
Global.brush_type_window_position = "right"
|
||||
|
||||
|
||||
func _on_LeftBrushSizeEdit_value_changed(value) -> void:
|
||||
Global.left_brush_size_edit.value = value
|
||||
Global.left_brush_size_slider.value = value
|
||||
var new_size = int(value)
|
||||
Global.left_brush_size = new_size
|
||||
update_left_custom_brush()
|
||||
|
||||
|
||||
func _on_RightBrushSizeEdit_value_changed(value) -> void:
|
||||
Global.right_brush_size_edit.value = value
|
||||
Global.right_brush_size_slider.value = value
|
||||
var new_size = int(value)
|
||||
Global.right_brush_size = new_size
|
||||
update_right_custom_brush()
|
||||
|
||||
|
||||
func _on_Brush_Selected() -> void:
|
||||
$BrushesPopup.hide()
|
||||
|
||||
|
||||
func _on_ColorSwitch_pressed() -> void:
|
||||
var temp: Color = Global.left_color_picker.color
|
||||
Global.left_color_picker.color = Global.right_color_picker.color
|
||||
Global.right_color_picker.color = temp
|
||||
update_left_custom_brush()
|
||||
update_right_custom_brush()
|
||||
|
||||
|
||||
func _on_ColorDefaults_pressed() -> void:
|
||||
Global.left_color_picker.color = Color.black
|
||||
Global.right_color_picker.color = Color.white
|
||||
update_left_custom_brush()
|
||||
update_right_custom_brush()
|
||||
|
||||
|
||||
func _on_LeftColorPickerButton_color_changed(color : Color) -> void:
|
||||
# If the color changed while it's on full transparency, make it opaque (GH issue #54)
|
||||
if color.a == 0:
|
||||
if previous_left_color.r != color.r or previous_left_color.g != color.g or previous_left_color.b != color.b:
|
||||
Global.left_color_picker.color.a = 1
|
||||
update_left_custom_brush()
|
||||
previous_left_color = color
|
||||
|
||||
|
||||
func _on_RightColorPickerButton_color_changed(color : Color) -> void:
|
||||
# If the color changed while it's on full transparency, make it opaque (GH issue #54)
|
||||
if color.a == 0:
|
||||
if previous_right_color.r != color.r or previous_right_color.g != color.g or previous_right_color.b != color.b:
|
||||
Global.right_color_picker.color.a = 1
|
||||
update_right_custom_brush()
|
||||
previous_right_color = color
|
||||
|
||||
|
||||
func _on_LeftInterpolateFactor_value_changed(value : float) -> void:
|
||||
Global.left_interpolate_spinbox.value = value
|
||||
Global.left_interpolate_slider.value = value
|
||||
update_left_custom_brush()
|
||||
|
||||
|
||||
func _on_RightInterpolateFactor_value_changed(value : float) -> void:
|
||||
Global.right_interpolate_spinbox.value = value
|
||||
Global.right_interpolate_slider.value = value
|
||||
update_right_custom_brush()
|
||||
|
||||
|
||||
func update_left_custom_brush() -> void:
|
||||
Global.update_left_custom_brush()
|
||||
|
||||
|
||||
func update_right_custom_brush() -> void:
|
||||
Global.update_right_custom_brush()
|
||||
|
||||
|
||||
func _on_LeftFillAreaOptions_item_selected(ID : int) -> void:
|
||||
Global.left_fill_area = ID
|
||||
|
||||
|
||||
func _on_LeftFillWithOptions_item_selected(ID : int) -> void:
|
||||
Global.left_fill_with = ID
|
||||
if ID == 1:
|
||||
Global.left_fill_pattern_container.visible = true
|
||||
else:
|
||||
Global.left_fill_pattern_container.visible = false
|
||||
|
||||
|
||||
func _on_LeftPatternTypeButton_pressed() -> void:
|
||||
Global.pattern_window_position = "left"
|
||||
Global.patterns_popup.popup(Rect2(Global.left_brush_type_button.rect_global_position, Vector2(226, 72)))
|
||||
|
||||
|
||||
func _on_LeftPatternOffsetX_value_changed(value : float) -> void:
|
||||
Global.left_fill_pattern_offset.x = value
|
||||
|
||||
|
||||
func _on_LeftPatternOffsetY_value_changed(value : float) -> void:
|
||||
Global.left_fill_pattern_offset.y = value
|
||||
|
||||
|
||||
func _on_RightPatternOffsetX_value_changed(value : float) -> void:
|
||||
Global.right_fill_pattern_offset.x = value
|
||||
|
||||
|
||||
func _on_RightPatternOffsetY_value_changed(value : float) -> void:
|
||||
Global.right_fill_pattern_offset.y = value
|
||||
|
||||
|
||||
func _on_RightFillAreaOptions_item_selected(ID : int) -> void:
|
||||
Global.right_fill_area = ID
|
||||
|
||||
|
||||
func _on_RightFillWithOptions_item_selected(ID : int) -> void:
|
||||
Global.right_fill_with = ID
|
||||
if ID == 1:
|
||||
Global.right_fill_pattern_container.visible = true
|
||||
else:
|
||||
Global.right_fill_pattern_container.visible = false
|
||||
|
||||
|
||||
func _on_RightPatternTypeButton_pressed() -> void:
|
||||
Global.pattern_window_position = "right"
|
||||
Global.patterns_popup.popup(Rect2(Global.right_brush_type_button.rect_global_position, Vector2(226, 72)))
|
||||
|
||||
|
||||
func _on_LeftLightenDarken_item_selected(ID : int) -> void:
|
||||
Global.left_ld = ID
|
||||
|
||||
|
||||
func _on_LeftLDAmountSpinbox_value_changed(value : float) -> void:
|
||||
Global.left_ld_amount = value / 100
|
||||
Global.left_ld_amount_slider.value = value
|
||||
Global.left_ld_amount_spinbox.value = value
|
||||
|
||||
|
||||
func _on_RightLightenDarken_item_selected(ID : int) -> void:
|
||||
Global.right_ld = ID
|
||||
|
||||
|
||||
func _on_RightLDAmountSpinbox_value_changed(value : float) -> void:
|
||||
Global.right_ld_amount = value / 100
|
||||
Global.right_ld_amount_slider.value = value
|
||||
Global.right_ld_amount_spinbox.value = value
|
||||
|
||||
|
||||
func _on_LeftForColorOptions_item_selected(ID : int) -> void:
|
||||
Global.left_color_picker_for = ID
|
||||
|
||||
|
||||
func _on_RightForColorOptions_item_selected(ID : int) -> void:
|
||||
Global.right_color_picker_for = ID
|
||||
|
||||
|
||||
func _on_LeftZoomModeOptions_item_selected(ID : int) -> void:
|
||||
Global.left_zoom_mode = ID
|
||||
|
||||
|
||||
func _on_RightZoomModeOptions_item_selected(ID : int) -> void:
|
||||
Global.right_zoom_mode = ID
|
||||
|
||||
|
||||
func _on_FitToFrameButton_pressed() -> void:
|
||||
var bigger_canvas_axis = max(Global.canvas.size.x, Global.canvas.size.y)
|
||||
var smaller_viewport_axis = min(Global.main_viewport.rect_size.x, Global.main_viewport.rect_size.y)
|
||||
Global.camera.zoom = Vector2(bigger_canvas_axis, bigger_canvas_axis) / smaller_viewport_axis
|
||||
Global.camera.offset = Global.canvas.size / 2
|
||||
Global.zoom_level_label.text = str(round(100 / Global.camera.zoom.x)) + " %"
|
||||
Global.horizontal_ruler.update()
|
||||
Global.vertical_ruler.update()
|
||||
|
||||
|
||||
func _on_100ZoomButton_pressed() -> void:
|
||||
Global.camera.zoom = Vector2.ONE
|
||||
Global.camera.offset = Global.canvas.size / 2
|
||||
Global.zoom_level_label.text = str(round(100 / Global.camera.zoom.x)) + " %"
|
||||
Global.horizontal_ruler.update()
|
||||
Global.vertical_ruler.update()
|
||||
|
||||
|
||||
func _on_LeftHorizontalMirroring_toggled(button_pressed) -> void:
|
||||
Global.left_horizontal_mirror = button_pressed
|
||||
|
||||
|
||||
func _on_LeftVerticalMirroring_toggled(button_pressed) -> void:
|
||||
Global.left_vertical_mirror = button_pressed
|
||||
|
||||
|
||||
func _on_RightHorizontalMirroring_toggled(button_pressed) -> void:
|
||||
Global.right_horizontal_mirror = button_pressed
|
||||
|
||||
|
||||
func _on_RightVerticalMirroring_toggled(button_pressed) -> void:
|
||||
Global.right_vertical_mirror = button_pressed
|
||||
|
||||
|
||||
func show_quit_dialog() -> void:
|
||||
if !$QuitDialog.visible:
|
||||
if !Global.project_has_changed:
|
||||
$QuitDialog.call_deferred("popup_centered")
|
||||
else:
|
||||
$QuitAndSaveDialog.call_deferred("popup_centered")
|
||||
|
||||
Global.can_draw = false
|
||||
|
||||
|
||||
func _on_QuitAndSaveDialog_custom_action(action : String) -> void:
|
||||
if action == "Save":
|
||||
is_quitting_on_save = true
|
||||
$SaveSprite.popup_centered()
|
||||
$QuitDialog.hide()
|
||||
Global.can_draw = false
|
||||
OpenSave.remove_backup()
|
||||
|
||||
|
||||
func _on_QuitDialog_confirmed() -> void:
|
||||
# Darken the UI to denote that the application is currently exiting
|
||||
# (it won't respond to user input in this state).
|
||||
modulate = Color(0.5, 0.5, 0.5)
|
||||
OpenSave.remove_backup()
|
||||
get_tree().quit()
|
||||
|
||||
|
||||
func _on_BackupConfirmation_confirmed(project_path : String, backup_path : String) -> void:
|
||||
OpenSave.reload_backup_file(project_path, backup_path)
|
||||
OpenSave.autosave_timer.start()
|
||||
$ExportDialog.file_name = OpenSave.current_save_path.get_file().trim_suffix(".pxo")
|
||||
$ExportDialog.directory_path = OpenSave.current_save_path.get_base_dir()
|
||||
$ExportDialog.was_exported = false
|
||||
file_menu.set_item_text(3, tr("Save") + " %s" % OpenSave.current_save_path.get_file())
|
||||
file_menu.set_item_text(6, tr("Export"))
|
||||
|
||||
|
||||
func _on_BackupConfirmation_delete(project_path : String, backup_path : String) -> void:
|
||||
OpenSave.remove_backup_by_path(project_path, backup_path)
|
||||
OpenSave.autosave_timer.start()
|
||||
# Reopen last project
|
||||
if Global.open_last_project:
|
||||
load_last_project()
|
||||
|
||||
|
||||
func _on_LeftPixelPerfectMode_toggled(button_pressed : bool) -> void:
|
||||
Global.left_pixel_perfect = button_pressed
|
||||
|
||||
|
||||
func _on_RightPixelPerfectMode_toggled(button_pressed : bool) -> void:
|
||||
Global.right_pixel_perfect = button_pressed
|
1773
src/Main.tscn
Normal file
1773
src/Main.tscn
Normal file
File diff suppressed because one or more lines are too long
12
src/NotificationLabel.gd
Normal file
12
src/NotificationLabel.gd
Normal file
|
@ -0,0 +1,12 @@
|
|||
extends Label
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
var tween := $Tween
|
||||
tween.interpolate_property(self, "rect_position", rect_position, Vector2(rect_position.x, rect_position.y - 100), 1, Tween.TRANS_LINEAR, Tween.EASE_OUT)
|
||||
tween.interpolate_property(self, "modulate", modulate, Color(modulate.r, modulate.g, modulate.b, 0), 1, Tween.TRANS_LINEAR, Tween.EASE_OUT)
|
||||
tween.start()
|
||||
|
||||
|
||||
func _on_Timer_timeout() -> void:
|
||||
queue_free()
|
21
src/NotificationLabel.tscn
Normal file
21
src/NotificationLabel.tscn
Normal file
|
@ -0,0 +1,21 @@
|
|||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://src/NotificationLabel.gd" type="Script" id=2]
|
||||
|
||||
|
||||
[node name="NotificationLabel" type="Label"]
|
||||
margin_right = 116.0
|
||||
margin_bottom = 14.0
|
||||
custom_colors/font_color_shadow = Color( 0, 0, 0, 1 )
|
||||
text = "Undo: Notification"
|
||||
script = ExtResource( 2 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Tween" type="Tween" parent="."]
|
||||
|
||||
[node name="Timer" type="Timer" parent="."]
|
||||
one_shot = true
|
||||
autostart = true
|
||||
[connection signal="timeout" from="Timer" to="." method="_on_Timer_timeout"]
|
190
src/Palette/EditPalettePopup.gd
Normal file
190
src/Palette/EditPalettePopup.gd
Normal file
|
@ -0,0 +1,190 @@
|
|||
extends WindowDialog
|
||||
|
||||
var palette_button = preload("res://src/Palette/PaletteButton.tscn")
|
||||
|
||||
var current_palette : String
|
||||
var current_swatch := -1
|
||||
var working_palette : Palette
|
||||
|
||||
onready var color_picker = $VBoxContainer/HBoxContainer/EditPaletteColorPicker
|
||||
onready var palette_grid = $VBoxContainer/HBoxContainer/VBoxContainer/Panel/ScrollContainer/EditPaletteGridContainer
|
||||
onready var color_name_edit = $VBoxContainer/PaletteOptions/EditPaletteColorNameLineEdit
|
||||
onready var palette_name_edit = $VBoxContainer/PaletteOptions/EditPaletteNameLineEdit
|
||||
onready var left_color_button = $VBoxContainer/HBoxContainer/VBoxContainer/CenterContainer/HBoxContainer/LeftColor/NinePatchRect
|
||||
onready var right_color_button = $VBoxContainer/HBoxContainer/VBoxContainer/CenterContainer/HBoxContainer/RightColor/NinePatchRect
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
$VBoxContainer/HBoxContainer/EditPaletteColorPicker.presets_visible = false
|
||||
|
||||
|
||||
func open(palette : String) -> void:
|
||||
current_palette = palette
|
||||
palette_name_edit.text = current_palette
|
||||
if Global.palettes.has(palette):
|
||||
working_palette = Global.palettes[palette].duplicate()
|
||||
_display_palette()
|
||||
Global.can_draw = false
|
||||
self.popup_centered()
|
||||
|
||||
left_color_button.modulate = Global.left_color_picker.color
|
||||
right_color_button.modulate = Global.right_color_picker.color
|
||||
|
||||
|
||||
func _display_palette() -> void:
|
||||
_clear_swatches()
|
||||
var index := 0
|
||||
|
||||
for color_data in working_palette.colors:
|
||||
var color = color_data.color
|
||||
var new_button = palette_button.instance()
|
||||
|
||||
new_button.color = color
|
||||
new_button.get_child(0).modulate = color
|
||||
new_button.hint_tooltip = color_data.data.to_upper() + " " + color_data.name
|
||||
new_button.draggable = true
|
||||
new_button.index = index
|
||||
new_button.connect("on_drop_data", self, "on_move_swatch")
|
||||
new_button.connect("pressed", self, "on_swatch_select", [new_button])
|
||||
|
||||
palette_grid.add_child(new_button)
|
||||
index += 1
|
||||
|
||||
if index > 0: # If there are colors, select the first
|
||||
on_swatch_select(palette_grid.get_child(0))
|
||||
|
||||
|
||||
func _clear_swatches() -> void:
|
||||
for child in palette_grid.get_children():
|
||||
if child is BaseButton:
|
||||
child.disconnect("on_drop_data", self, "on_move_swatch")
|
||||
child.queue_free()
|
||||
|
||||
|
||||
func on_swatch_select(new_button) -> void:
|
||||
current_swatch = new_button.index
|
||||
color_name_edit.text = working_palette.get_color_name(current_swatch)
|
||||
color_picker.color = working_palette.get_color(current_swatch)
|
||||
|
||||
|
||||
func on_move_swatch(from : int, to : int) -> void:
|
||||
working_palette.move_color(from, to)
|
||||
palette_grid.move_child(palette_grid.get_child(from), to)
|
||||
current_swatch = to
|
||||
|
||||
re_index_swatches()
|
||||
|
||||
|
||||
func _on_AddSwatchButton_pressed() -> void:
|
||||
var color : Color = color_picker.color
|
||||
var new_index : int = working_palette.colors.size()
|
||||
working_palette.add_color(color)
|
||||
|
||||
var new_button = palette_button.instance()
|
||||
|
||||
new_button.color = color
|
||||
new_button.get_child(0).modulate = color
|
||||
new_button.hint_tooltip = "#" + working_palette.get_color_data(new_index).to_upper() + " " + working_palette.get_color_name(new_index)
|
||||
new_button.draggable = true
|
||||
var index : int = palette_grid.get_child_count()
|
||||
new_button.index = index
|
||||
new_button.connect("on_drop_data", self, "on_move_swatch")
|
||||
new_button.connect("pressed", self, "on_swatch_select", [new_button])
|
||||
|
||||
palette_grid.add_child(new_button)
|
||||
on_swatch_select(new_button)
|
||||
|
||||
|
||||
func _on_RemoveSwatchButton_pressed() -> void:
|
||||
if working_palette.colors.size() > 0:
|
||||
working_palette.remove_color(current_swatch)
|
||||
palette_grid.remove_child(palette_grid.get_child(current_swatch))
|
||||
re_index_swatches()
|
||||
|
||||
if current_swatch == working_palette.colors.size():
|
||||
current_swatch -= 1
|
||||
|
||||
if current_swatch >= 0:
|
||||
on_swatch_select(palette_grid.get_child(current_swatch))
|
||||
|
||||
|
||||
func re_index_swatches() -> void:
|
||||
# Re-index swatches with new order
|
||||
var index := 0
|
||||
for child in palette_grid.get_children():
|
||||
child.index = index
|
||||
index += 1
|
||||
|
||||
|
||||
# Rename a palette, copying to user directory if necessary.
|
||||
func rename_palette_file_with_priority_dirs(old_fname: String, new_fname: String) -> void:
|
||||
var user_write_directory: String = Global.directory_module.get_palette_write_path()
|
||||
var usrwrite_dir := Directory.new()
|
||||
usrwrite_dir.open(user_write_directory)
|
||||
if usrwrite_dir.file_exists(old_fname):
|
||||
usrwrite_dir.rename(old_fname, new_fname)
|
||||
else:
|
||||
# Scan through the main system directories
|
||||
var priority_dirs : Array = Global.directory_module.get_palette_search_path_in_order()
|
||||
var best_clone_location = Global.palette_container.get_best_palette_file_location(
|
||||
priority_dirs,
|
||||
old_fname
|
||||
)
|
||||
if best_clone_location != null:
|
||||
usrwrite_dir.copy(best_clone_location, new_fname)
|
||||
|
||||
|
||||
func _on_EditPaletteSaveButton_pressed() -> void:
|
||||
if palette_name_edit.text != current_palette:
|
||||
Global.palettes.erase(current_palette)
|
||||
rename_palette_file_with_priority_dirs(
|
||||
current_palette + ".json",
|
||||
palette_name_edit.text + ".json"
|
||||
)
|
||||
current_palette = palette_name_edit.text
|
||||
working_palette.name = current_palette
|
||||
|
||||
var optionbutton_index = Global.palette_option_button.selected
|
||||
Global.palette_option_button.set_item_text(optionbutton_index, current_palette)
|
||||
Global.palette_option_button.set_item_metadata(optionbutton_index, current_palette)
|
||||
Global.palette_option_button.text = current_palette
|
||||
|
||||
Global.palettes[current_palette] = working_palette
|
||||
Global.palette_container.on_palette_select(current_palette)
|
||||
Global.palette_container.save_palette(current_palette, working_palette.name + ".json")
|
||||
self.hide()
|
||||
|
||||
|
||||
func _on_EditPaletteCancelButton_pressed() -> void:
|
||||
self.hide()
|
||||
|
||||
|
||||
func _on_EditPaletteColorNameLineEdit_text_changed(new_text : String) -> void:
|
||||
if current_swatch >= 0 && current_swatch < working_palette.colors.size():
|
||||
working_palette.set_color_name(current_swatch, new_text)
|
||||
_refresh_hint_tooltip(current_swatch)
|
||||
|
||||
|
||||
func _on_EditPaletteColorPicker_color_changed(color : Color) -> void:
|
||||
if current_swatch >= 0 && current_swatch < working_palette.colors.size():
|
||||
palette_grid.get_child(current_swatch).get_child(0).modulate = color
|
||||
working_palette.set_color(current_swatch, color)
|
||||
_refresh_hint_tooltip(current_swatch)
|
||||
|
||||
|
||||
func _refresh_hint_tooltip(_index : int) -> void:
|
||||
palette_grid.get_child(current_swatch).hint_tooltip = "#" + working_palette.get_color_data(current_swatch).to_upper() + " " + working_palette.get_color_name(current_swatch)
|
||||
|
||||
|
||||
func _on_LeftColor_pressed() -> void:
|
||||
color_picker.color = Global.left_color_picker.color
|
||||
_on_EditPaletteColorPicker_color_changed(color_picker.color)
|
||||
|
||||
|
||||
func _on_RightColor_pressed() -> void:
|
||||
color_picker.color = Global.right_color_picker.color
|
||||
_on_EditPaletteColorPicker_color_changed(color_picker.color)
|
||||
|
||||
|
||||
func _on_EditPalettePopup_popup_hide() -> void:
|
||||
Global.can_draw = true
|
240
src/Palette/EditPalettePopup.tscn
Normal file
240
src/Palette/EditPalettePopup.tscn
Normal file
|
@ -0,0 +1,240 @@
|
|||
[gd_scene load_steps=5 format=2]
|
||||
|
||||
[ext_resource path="res://src/Palette/EditPalettePopup.gd" type="Script" id=1]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Timeline/new_frame.png" type="Texture" id=2]
|
||||
[ext_resource path="res://Assets/Graphics/Dark Themes/Timeline/remove_frame.png" type="Texture" id=3]
|
||||
[ext_resource path="res://Assets/Graphics/Palette/palette_button_fill.png" type="Texture" id=6]
|
||||
|
||||
[node name="EditPalettePopup" type="WindowDialog"]
|
||||
margin_right = 600.0
|
||||
margin_bottom = 550.0
|
||||
rect_min_size = Vector2( 600, 570 )
|
||||
window_title = "Edit Palette"
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
margin_left = 10.0
|
||||
margin_top = 10.0
|
||||
margin_right = -10.0
|
||||
margin_bottom = -10.0
|
||||
size_flags_horizontal = 3
|
||||
custom_constants/separation = 8
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
|
||||
margin_right = 580.0
|
||||
margin_bottom = 462.0
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="EditPaletteColorPicker" type="ColorPicker" parent="VBoxContainer/HBoxContainer"]
|
||||
margin_left = 4.0
|
||||
margin_top = 4.0
|
||||
margin_right = 4.0
|
||||
margin_bottom = 4.0
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/HBoxContainer"]
|
||||
margin_left = 312.0
|
||||
margin_right = 556.0
|
||||
margin_bottom = 462.0
|
||||
size_flags_horizontal = 3
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Panel" type="Panel" parent="VBoxContainer/HBoxContainer/VBoxContainer"]
|
||||
margin_right = 244.0
|
||||
margin_bottom = 408.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="VBoxContainer/HBoxContainer/VBoxContainer/Panel"]
|
||||
margin_right = 244.0
|
||||
margin_bottom = 438.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="EditPaletteGridContainer" type="GridContainer" parent="VBoxContainer/HBoxContainer/VBoxContainer/Panel/ScrollContainer"]
|
||||
margin_right = 244.0
|
||||
margin_bottom = 438.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
columns = 8
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/VBoxContainer"]
|
||||
margin_top = 412.0
|
||||
margin_right = 244.0
|
||||
margin_bottom = 426.0
|
||||
text = "Use current left & right colors"
|
||||
align = 1
|
||||
|
||||
[node name="CenterContainer" type="CenterContainer" parent="VBoxContainer/HBoxContainer/VBoxContainer"]
|
||||
margin_top = 430.0
|
||||
margin_right = 244.0
|
||||
margin_bottom = 462.0
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/HBoxContainer/VBoxContainer/CenterContainer"]
|
||||
margin_left = 56.0
|
||||
margin_right = 188.0
|
||||
margin_bottom = 32.0
|
||||
|
||||
[node name="LeftColor" type="Button" parent="VBoxContainer/HBoxContainer/VBoxContainer/CenterContainer/HBoxContainer"]
|
||||
margin_right = 64.0
|
||||
margin_bottom = 32.0
|
||||
rect_min_size = Vector2( 64, 32 )
|
||||
mouse_default_cursor_shape = 2
|
||||
|
||||
[node name="NinePatchRect" type="NinePatchRect" parent="VBoxContainer/HBoxContainer/VBoxContainer/CenterContainer/HBoxContainer/LeftColor"]
|
||||
modulate = Color( 0, 0, 0, 1 )
|
||||
margin_left = 2.0
|
||||
margin_top = 3.0
|
||||
margin_right = 62.0
|
||||
margin_bottom = 29.0
|
||||
texture = ExtResource( 6 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="RightColor" type="Button" parent="VBoxContainer/HBoxContainer/VBoxContainer/CenterContainer/HBoxContainer"]
|
||||
margin_left = 68.0
|
||||
margin_right = 132.0
|
||||
margin_bottom = 32.0
|
||||
rect_min_size = Vector2( 64, 32 )
|
||||
mouse_default_cursor_shape = 2
|
||||
|
||||
[node name="NinePatchRect" type="NinePatchRect" parent="VBoxContainer/HBoxContainer/VBoxContainer/CenterContainer/HBoxContainer/RightColor"]
|
||||
margin_left = 2.0
|
||||
margin_top = 3.0
|
||||
margin_right = 62.0
|
||||
margin_bottom = 29.0
|
||||
texture = ExtResource( 6 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="ColorButtons" type="VBoxContainer" parent="VBoxContainer/HBoxContainer"]
|
||||
margin_left = 560.0
|
||||
margin_right = 580.0
|
||||
margin_bottom = 462.0
|
||||
|
||||
[node name="AddSwatchButton" type="Button" parent="VBoxContainer/HBoxContainer/ColorButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_right = 20.0
|
||||
margin_bottom = 20.0
|
||||
mouse_default_cursor_shape = 2
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="VBoxContainer/HBoxContainer/ColorButtons/AddSwatchButton"]
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
margin_left = -6.0
|
||||
margin_top = -6.0
|
||||
margin_right = 6.0
|
||||
margin_bottom = 6.0
|
||||
texture = ExtResource( 2 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="RemoveSwatchButton" type="Button" parent="VBoxContainer/HBoxContainer/ColorButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_top = 24.0
|
||||
margin_right = 20.0
|
||||
margin_bottom = 44.0
|
||||
rect_min_size = Vector2( 20, 0 )
|
||||
mouse_default_cursor_shape = 2
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="VBoxContainer/HBoxContainer/ColorButtons/RemoveSwatchButton"]
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
margin_left = -6.0
|
||||
margin_top = -1.0
|
||||
margin_right = 6.0
|
||||
margin_bottom = 1.0
|
||||
texture = ExtResource( 3 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="PaletteOptions" type="GridContainer" parent="VBoxContainer"]
|
||||
margin_top = 470.0
|
||||
margin_right = 580.0
|
||||
margin_bottom = 522.0
|
||||
columns = 2
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer/PaletteOptions"]
|
||||
margin_top = 5.0
|
||||
margin_right = 91.0
|
||||
margin_bottom = 19.0
|
||||
text = "Color Name:"
|
||||
|
||||
[node name="EditPaletteColorNameLineEdit" type="LineEdit" parent="VBoxContainer/PaletteOptions"]
|
||||
margin_left = 95.0
|
||||
margin_right = 580.0
|
||||
margin_bottom = 24.0
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="Label2" type="Label" parent="VBoxContainer/PaletteOptions"]
|
||||
margin_top = 33.0
|
||||
margin_right = 91.0
|
||||
margin_bottom = 47.0
|
||||
text = "Palette Name:"
|
||||
|
||||
[node name="EditPaletteNameLineEdit" type="LineEdit" parent="VBoxContainer/PaletteOptions"]
|
||||
margin_left = 95.0
|
||||
margin_top = 28.0
|
||||
margin_right = 580.0
|
||||
margin_bottom = 52.0
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="WindowOptionsContainer" type="HBoxContainer" parent="VBoxContainer"]
|
||||
margin_top = 530.0
|
||||
margin_right = 580.0
|
||||
margin_bottom = 550.0
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="SpacerControl" type="Control" parent="VBoxContainer/WindowOptionsContainer"]
|
||||
margin_right = 156.0
|
||||
margin_bottom = 20.0
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="EditPaletteSaveButton" type="Button" parent="VBoxContainer/WindowOptionsContainer"]
|
||||
margin_left = 160.0
|
||||
margin_right = 201.0
|
||||
margin_bottom = 20.0
|
||||
text = "Save"
|
||||
|
||||
[node name="SpacerControl2" type="Control" parent="VBoxContainer/WindowOptionsContainer"]
|
||||
margin_left = 205.0
|
||||
margin_right = 361.0
|
||||
margin_bottom = 20.0
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="EditPaletteCancelButton" type="Button" parent="VBoxContainer/WindowOptionsContainer"]
|
||||
margin_left = 365.0
|
||||
margin_right = 419.0
|
||||
margin_bottom = 20.0
|
||||
text = "Cancel"
|
||||
|
||||
[node name="SpacerControl3" type="Control" parent="VBoxContainer/WindowOptionsContainer"]
|
||||
margin_left = 423.0
|
||||
margin_right = 580.0
|
||||
margin_bottom = 20.0
|
||||
size_flags_horizontal = 3
|
||||
[connection signal="popup_hide" from="." to="." method="_on_EditPalettePopup_popup_hide"]
|
||||
[connection signal="color_changed" from="VBoxContainer/HBoxContainer/EditPaletteColorPicker" to="." method="_on_EditPaletteColorPicker_color_changed"]
|
||||
[connection signal="pressed" from="VBoxContainer/HBoxContainer/VBoxContainer/CenterContainer/HBoxContainer/LeftColor" to="." method="_on_LeftColor_pressed"]
|
||||
[connection signal="pressed" from="VBoxContainer/HBoxContainer/VBoxContainer/CenterContainer/HBoxContainer/RightColor" to="." method="_on_RightColor_pressed"]
|
||||
[connection signal="pressed" from="VBoxContainer/HBoxContainer/ColorButtons/AddSwatchButton" to="." method="_on_AddSwatchButton_pressed"]
|
||||
[connection signal="pressed" from="VBoxContainer/HBoxContainer/ColorButtons/RemoveSwatchButton" to="." method="_on_RemoveSwatchButton_pressed"]
|
||||
[connection signal="text_changed" from="VBoxContainer/PaletteOptions/EditPaletteColorNameLineEdit" to="." method="_on_EditPaletteColorNameLineEdit_text_changed"]
|
||||
[connection signal="pressed" from="VBoxContainer/WindowOptionsContainer/EditPaletteSaveButton" to="." method="_on_EditPaletteSaveButton_pressed"]
|
||||
[connection signal="pressed" from="VBoxContainer/WindowOptionsContainer/EditPaletteCancelButton" to="." method="_on_EditPaletteCancelButton_pressed"]
|
25
src/Palette/NewPaletteDialog.tscn
Normal file
25
src/Palette/NewPaletteDialog.tscn
Normal file
|
@ -0,0 +1,25 @@
|
|||
[gd_scene format=2]
|
||||
|
||||
[node name="NewPaletteDialog" type="ConfirmationDialog"]
|
||||
margin_right = 200.0
|
||||
margin_bottom = 70.0
|
||||
window_title = "Create a new custom palette from existing default?"
|
||||
|
||||
[node name="HBoxContainer2" type="HBoxContainer" parent="."]
|
||||
margin_left = 8.0
|
||||
margin_top = 8.0
|
||||
margin_right = 365.0
|
||||
margin_bottom = 34.0
|
||||
|
||||
[node name="Label" type="Label" parent="HBoxContainer2"]
|
||||
margin_top = 6.0
|
||||
margin_right = 91.0
|
||||
margin_bottom = 20.0
|
||||
text = "Palette Name:"
|
||||
|
||||
[node name="NewPaletteNameLineEdit" type="LineEdit" parent="HBoxContainer2"]
|
||||
margin_left = 95.0
|
||||
margin_right = 357.0
|
||||
margin_bottom = 26.0
|
||||
size_flags_horizontal = 3
|
||||
expand_to_text_length = true
|
161
src/Palette/Palette.gd
Normal file
161
src/Palette/Palette.gd
Normal file
|
@ -0,0 +1,161 @@
|
|||
class_name Palette
|
||||
extends Reference
|
||||
|
||||
|
||||
var name : String = "Custom_Palette"
|
||||
var colors : Array = []
|
||||
var comments : String = ""
|
||||
var editable : bool = true
|
||||
|
||||
|
||||
func get_class() -> String:
|
||||
return "Palette"
|
||||
|
||||
|
||||
func is_class(_name : String) -> bool:
|
||||
return _name == "Palette" or .is_class(_name)
|
||||
|
||||
|
||||
func insert_color(index : int, new_color : Color, _name : String = "no name") -> void:
|
||||
if index <= colors.size():
|
||||
var c := PaletteColor.new(new_color, _name)
|
||||
colors.insert(index, c)
|
||||
|
||||
|
||||
func add_color(new_color : Color, _name : String = "no name") -> void:
|
||||
var c := PaletteColor.new(new_color, _name)
|
||||
colors.push_back(c)
|
||||
|
||||
|
||||
func remove_color(index : int) -> void:
|
||||
if index < colors.size():
|
||||
colors.remove(index)
|
||||
|
||||
|
||||
func move_color(from : int, to : int) -> void:
|
||||
if from < colors.size() && to < colors.size():
|
||||
var c : PaletteColor = colors[from]
|
||||
remove_color(from)
|
||||
insert_color(to, c.color, c.name)
|
||||
|
||||
|
||||
func get_color(index : int) -> Color:
|
||||
var result := Color.black
|
||||
|
||||
if index < colors.size():
|
||||
result = colors[index].color
|
||||
|
||||
return result
|
||||
|
||||
|
||||
func set_color(index : int, new_color : Color) -> void:
|
||||
if index < colors.size():
|
||||
colors[index].color = new_color
|
||||
|
||||
|
||||
func get_color_data(index : int) -> String:
|
||||
var result := ""
|
||||
|
||||
if index < colors.size():
|
||||
result = colors[index].data
|
||||
|
||||
return result
|
||||
|
||||
|
||||
func has_color(color: Color) -> bool:
|
||||
for palette_color in colors:
|
||||
if palette_color.color == color:
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
func set_color_data(index : int, new_color : String) -> void:
|
||||
if index < colors.size():
|
||||
colors[index].data = new_color
|
||||
|
||||
|
||||
func get_color_name(index : int) -> String:
|
||||
var result = ""
|
||||
|
||||
if index < colors.size():
|
||||
result = colors[index].name
|
||||
|
||||
return result
|
||||
|
||||
|
||||
func set_color_name(index : int, new_name : String) -> void:
|
||||
if index < colors.size():
|
||||
colors[index].name = new_name
|
||||
|
||||
|
||||
func save_to_file(path : String) -> void:
|
||||
var file = File.new()
|
||||
file.open(path, File.WRITE)
|
||||
file.store_string(_serialize())
|
||||
file.close()
|
||||
|
||||
|
||||
func duplicate(): # -> Palette
|
||||
var copy = get_script().new() # : Palette
|
||||
copy.name = name
|
||||
copy.comments = comments
|
||||
copy.editable = editable
|
||||
for color in colors:
|
||||
copy.colors.push_back(color.duplicate())
|
||||
return copy
|
||||
|
||||
|
||||
func _serialize() -> String:
|
||||
var result = ""
|
||||
var serialize_data : Dictionary = {
|
||||
"name" : name,
|
||||
"comments" : comments,
|
||||
"colors" : [],
|
||||
"editable" : editable
|
||||
}
|
||||
for color in colors:
|
||||
serialize_data.colors.push_back(color.toDict())
|
||||
|
||||
result = JSON.print(serialize_data, " ")
|
||||
|
||||
return result
|
||||
|
||||
|
||||
func deserialize(input_string : String): # -> Palette
|
||||
var result = get_script().new()
|
||||
|
||||
var result_json = JSON.parse(input_string)
|
||||
|
||||
if result_json.error != OK: # If parse has errors
|
||||
print("Error: ", result_json.error)
|
||||
print("Error Line: ", result_json.error_line)
|
||||
print("Error String: ", result_json.error_string)
|
||||
result = null
|
||||
else: # If parse OK
|
||||
var data = result_json.result
|
||||
if data.has("name"): # If data is 'valid' palette file
|
||||
result = get_script().new()
|
||||
result.name = data.name
|
||||
if data.has("comments"):
|
||||
result.comments = data.comments
|
||||
if data.has("editable"):
|
||||
result.editable = data.editable
|
||||
for color_data in data.colors:
|
||||
result.add_color(color_data.data, color_data.name)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
func load_from_file(path : String): # -> Palette
|
||||
var result = null # : Palette
|
||||
var file = File.new()
|
||||
|
||||
if file.file_exists(path):
|
||||
file.open(path, File.READ)
|
||||
|
||||
var text : String = file.get_as_text()
|
||||
result = deserialize(text)
|
||||
|
||||
file.close()
|
||||
|
||||
return result
|
27
src/Palette/PaletteButton.gd
Normal file
27
src/Palette/PaletteButton.gd
Normal file
|
@ -0,0 +1,27 @@
|
|||
extends Button
|
||||
signal on_drop_data
|
||||
|
||||
export var index := 0;
|
||||
export var color : Color = Color.white
|
||||
export var draggable := false
|
||||
|
||||
var drag_preview_texture = preload("res://Assets/Graphics/Palette/swatch_drag_preview.png")
|
||||
|
||||
|
||||
func get_drag_data(_position):
|
||||
var data = null
|
||||
if draggable:
|
||||
data = {source_index = index}
|
||||
var drag_icon = TextureRect.new()
|
||||
drag_icon.texture = drag_preview_texture
|
||||
drag_icon.modulate = color
|
||||
set_drag_preview(drag_icon)
|
||||
return data
|
||||
|
||||
|
||||
func can_drop_data(_position, _data) -> bool:
|
||||
return true
|
||||
|
||||
|
||||
func drop_data(_position, data) -> void:
|
||||
emit_signal("on_drop_data", data.source_index, index)
|
45
src/Palette/PaletteButton.tscn
Normal file
45
src/Palette/PaletteButton.tscn
Normal file
|
@ -0,0 +1,45 @@
|
|||
[gd_scene load_steps=8 format=2]
|
||||
|
||||
[ext_resource path="res://Themes & Styles/StyleBoxes/palette_stylebox_pressedr.tres" type="StyleBox" id=1]
|
||||
[ext_resource path="res://Themes & Styles/StyleBoxes/palette_stylebox_hover.tres" type="StyleBox" id=2]
|
||||
[ext_resource path="res://src/Palette/PaletteButton.gd" type="Script" id=3]
|
||||
[ext_resource path="res://Themes & Styles/StyleBoxes/palette_stylebox_focus.tres" type="StyleBox" id=4]
|
||||
[ext_resource path="res://Themes & Styles/StyleBoxes/palette_stylebox_normal.tres" type="StyleBox" id=5]
|
||||
[ext_resource path="res://Assets/Graphics/Palette/palette_button_fill.png" type="Texture" id=6]
|
||||
|
||||
[sub_resource type="ImageTexture" id=1]
|
||||
|
||||
[node name="PaletteButton" type="Button"]
|
||||
margin_right = 26.0
|
||||
margin_bottom = 26.0
|
||||
rect_min_size = Vector2( 26, 26 )
|
||||
hint_tooltip = "Color Name"
|
||||
custom_styles/hover = ExtResource( 2 )
|
||||
custom_styles/pressed = ExtResource( 1 )
|
||||
custom_styles/focus = ExtResource( 4 )
|
||||
custom_styles/normal = ExtResource( 5 )
|
||||
action_mode = 0
|
||||
button_mask = 3
|
||||
icon = SubResource( 1 )
|
||||
script = ExtResource( 3 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="NinePatchRect" type="NinePatchRect" parent="."]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
margin_left = 1.0
|
||||
margin_top = 1.0
|
||||
margin_right = -1.0
|
||||
margin_bottom = -1.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
texture = ExtResource( 6 )
|
||||
patch_margin_left = 2
|
||||
patch_margin_top = 2
|
||||
patch_margin_right = 2
|
||||
patch_margin_bottom = 2
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
54
src/Palette/PaletteColor.gd
Normal file
54
src/Palette/PaletteColor.gd
Normal file
|
@ -0,0 +1,54 @@
|
|||
class_name PaletteColor
|
||||
extends Reference
|
||||
|
||||
|
||||
var color : Color = Color.black setget _set_color
|
||||
var data : String = "" setget _set_data
|
||||
var name : String = "no name"
|
||||
|
||||
|
||||
func get_class() -> String:
|
||||
return "PaletteColor"
|
||||
|
||||
|
||||
func is_class(_name : String) -> bool:
|
||||
return _name == "PaletteColor" or .is_class(_name)
|
||||
|
||||
|
||||
func _init(new_color : Color = Color.black, new_name : String = "no name") -> void:
|
||||
self.color = new_color
|
||||
self.name = new_name
|
||||
|
||||
|
||||
func _set_color(new_value : Color) -> void:
|
||||
color = new_value
|
||||
data = color.to_html(true)
|
||||
|
||||
|
||||
func _set_data(new_value : String) -> void:
|
||||
data = new_value
|
||||
color = Color(data)
|
||||
|
||||
|
||||
func toDict() -> Dictionary:
|
||||
var result = {
|
||||
"data" : data,
|
||||
"name" : name
|
||||
}
|
||||
return result
|
||||
|
||||
|
||||
func fromDict(input_dict : Dictionary): # -> PaletteColor
|
||||
var result = get_script().new()
|
||||
|
||||
result.data = input_dict.data
|
||||
result.name = input_dict.name
|
||||
|
||||
return result
|
||||
|
||||
|
||||
func duplicate(): # -> PaletteColor
|
||||
var copy = get_script().new() # : PaletteColor
|
||||
copy.data = data
|
||||
copy.name = name
|
||||
return copy
|
272
src/Palette/PaletteContainer.gd
Normal file
272
src/Palette/PaletteContainer.gd
Normal file
|
@ -0,0 +1,272 @@
|
|||
extends GridContainer
|
||||
|
||||
const palette_button = preload("res://src/Palette/PaletteButton.tscn")
|
||||
|
||||
var current_palette = "Default"
|
||||
var from_palette : Palette
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_load_palettes()
|
||||
|
||||
# Select default palette "Default"
|
||||
on_palette_select(current_palette)
|
||||
|
||||
var add_palette_menu : PopupMenu = Global.add_palette_button.get_child(0)
|
||||
add_palette_menu.connect("id_pressed", self, "add_palette_menu_id_pressed")
|
||||
|
||||
|
||||
func _clear_swatches() -> void:
|
||||
for child in get_children():
|
||||
if child is BaseButton:
|
||||
child.disconnect("pressed", self, "on_color_select")
|
||||
child.queue_free()
|
||||
|
||||
|
||||
func on_palette_select(palette_name : String) -> void:
|
||||
_clear_swatches()
|
||||
if Global.palettes.has(palette_name): # Palette exists in memory
|
||||
current_palette = palette_name
|
||||
var palette : Palette = Global.palettes[palette_name]
|
||||
_display_palette(palette)
|
||||
|
||||
|
||||
func on_new_empty_palette() -> void:
|
||||
Global.new_palette_dialog.window_title = "Create a new empty palette?"
|
||||
Global.new_palette_name_line_edit.text = "Custom_Palette"
|
||||
from_palette = null
|
||||
Global.new_palette_dialog.popup_centered()
|
||||
Global.can_draw = false
|
||||
|
||||
|
||||
func on_import_palette() -> void:
|
||||
Global.palette_import_file_dialog.popup_centered()
|
||||
Global.can_draw = false
|
||||
|
||||
|
||||
func on_palette_import_file_selected(path : String) -> void:
|
||||
var palette : Palette = null
|
||||
if path.to_lower().ends_with("json"):
|
||||
palette = Palette.new().load_from_file(path)
|
||||
elif path.to_lower().ends_with("gpl"):
|
||||
palette = Import.import_gpl(path)
|
||||
elif path.to_lower().ends_with("png"):
|
||||
palette = Import.import_png_palette(path)
|
||||
|
||||
if palette:
|
||||
if not Global.palettes.has(palette.name):
|
||||
Global.palettes[palette.name] = palette
|
||||
Global.palette_option_button.add_item(palette.name)
|
||||
var index: int = Global.palette_option_button.get_item_count() - 1
|
||||
Global.palette_option_button.set_item_metadata(index, palette.name)
|
||||
Global.palette_option_button.select(index)
|
||||
on_palette_select(palette.name)
|
||||
save_palette(palette.name, palette.name + ".json")
|
||||
else:
|
||||
Global.error_dialog.set_text(tr("Error: Palette named '%s' already exists!") % palette.name)
|
||||
Global.error_dialog.popup_centered()
|
||||
else:
|
||||
Global.error_dialog.set_text("Invalid Palette file!")
|
||||
Global.error_dialog.popup_centered()
|
||||
|
||||
|
||||
func _on_AddPalette_pressed() -> void:
|
||||
Global.add_palette_button.get_child(0).popup(Rect2(Global.add_palette_button.rect_global_position, Vector2.ONE))
|
||||
|
||||
|
||||
func on_new_palette_confirmed() -> void:
|
||||
var new_palette_name : String = Global.new_palette_name_line_edit.text
|
||||
var result : String = create_new_palette(new_palette_name, from_palette)
|
||||
if not result.empty():
|
||||
Global.error_dialog.set_text(result)
|
||||
Global.error_dialog.popup_centered()
|
||||
|
||||
|
||||
func add_palette_menu_id_pressed(id : int) -> void:
|
||||
match id:
|
||||
0: # New Empty Palette
|
||||
Global.palette_container.on_new_empty_palette()
|
||||
1: # Import Palette
|
||||
Global.palette_container.on_import_palette()
|
||||
|
||||
|
||||
func create_new_palette(name : String, _from_palette : Palette) -> String: # Returns empty string, else error string
|
||||
var new_palette : Palette = Palette.new()
|
||||
|
||||
# Check if new name is valid
|
||||
if name.empty():
|
||||
return tr("Error: Palette must have a valid name.")
|
||||
if Global.palettes.has(name):
|
||||
return tr("Error: Palette named '%s' already exists!") % name
|
||||
|
||||
new_palette.name = name
|
||||
# Check if source palette has data
|
||||
if _from_palette:
|
||||
new_palette = _from_palette.duplicate()
|
||||
new_palette.name = name
|
||||
new_palette.editable = true
|
||||
|
||||
# Add palette to Global and options
|
||||
Global.palettes[name] = new_palette
|
||||
Global.palette_option_button.add_item(name)
|
||||
var index : int = Global.palette_option_button.get_item_count() - 1
|
||||
Global.palette_option_button.set_item_metadata(index, name)
|
||||
Global.palette_option_button.select(index)
|
||||
|
||||
save_palette(name, name + ".json")
|
||||
|
||||
on_palette_select(name)
|
||||
return ""
|
||||
|
||||
|
||||
func on_edit_palette() -> void:
|
||||
var palette : Palette = Global.palettes[current_palette]
|
||||
|
||||
var create_new_palette := true # Create new palette by default
|
||||
if palette.editable:
|
||||
create_new_palette = false # Edit if already a custom palette
|
||||
|
||||
if create_new_palette:
|
||||
from_palette = Global.palettes[current_palette]
|
||||
Global.new_palette_dialog.window_title = "Create a new custom palette from existing default?"
|
||||
Global.new_palette_name_line_edit.text = "Custom_" + current_palette
|
||||
Global.new_palette_dialog.popup_centered()
|
||||
Global.can_draw = false
|
||||
else:
|
||||
from_palette = null
|
||||
Global.edit_palette_popup.open(current_palette)
|
||||
|
||||
|
||||
func _on_PaletteOptionButton_item_selected(ID : int) -> void:
|
||||
var palette_name = Global.palette_option_button.get_item_metadata(ID)
|
||||
on_palette_select(palette_name)
|
||||
|
||||
|
||||
func _display_palette(palette : Palette) -> void:
|
||||
var index := 0
|
||||
|
||||
for color_data in palette.colors:
|
||||
var color = color_data.color
|
||||
var new_button = palette_button.instance()
|
||||
|
||||
new_button.get_child(0).modulate = color
|
||||
new_button.hint_tooltip = "#" + color_data.data.to_upper() + " " + color_data.name
|
||||
new_button.connect("pressed", self, "on_color_select", [index])
|
||||
|
||||
add_child(new_button)
|
||||
index += 1
|
||||
|
||||
|
||||
func on_color_select(index : int) -> void:
|
||||
var color : Color = Global.palettes[current_palette].get_color(index)
|
||||
|
||||
if Input.is_action_just_pressed("left_mouse"):
|
||||
Global.left_color_picker.color = color
|
||||
Global.update_left_custom_brush()
|
||||
elif Input.is_action_just_pressed("right_mouse"):
|
||||
Global.right_color_picker.color = color
|
||||
Global.update_right_custom_brush()
|
||||
|
||||
|
||||
func _load_palettes() -> void:
|
||||
Global.directory_module.ensure_xdg_user_dirs_exist()
|
||||
var search_locations = Global.directory_module.get_palette_search_path_in_order()
|
||||
var priority_ordered_files := get_palette_priority_file_map(search_locations)
|
||||
|
||||
# Iterate backwards, so any palettes defined in default files
|
||||
# get overwritten by those of the same name in user files
|
||||
search_locations.invert()
|
||||
priority_ordered_files.invert()
|
||||
for i in range(len(search_locations)):
|
||||
var base_directory : String = search_locations[i]
|
||||
var palette_files : Array = priority_ordered_files[i]
|
||||
for file_name in palette_files:
|
||||
var palette : Palette = Palette.new().load_from_file(base_directory.plus_file(file_name))
|
||||
if palette:
|
||||
Global.palettes[palette.name] = palette
|
||||
Global.palette_option_button.add_item(palette.name)
|
||||
var index: int = Global.palette_option_button.get_item_count() - 1
|
||||
Global.palette_option_button.set_item_metadata(index, palette.name)
|
||||
if palette.name == "Default":
|
||||
Global.palette_option_button.select(index)
|
||||
|
||||
if not "Default" in Global.palettes && Global.palettes.size() > 0:
|
||||
Global.palette_container._on_PaletteOptionButton_item_selected(0)
|
||||
|
||||
|
||||
# Get the palette files in a single directory.
|
||||
# if it does not exist, return []
|
||||
func get_palette_files(path : String ) -> Array:
|
||||
var dir := Directory.new()
|
||||
var results = []
|
||||
|
||||
if not dir.dir_exists(path):
|
||||
return []
|
||||
|
||||
dir.open(path)
|
||||
dir.list_dir_begin()
|
||||
|
||||
while true:
|
||||
var file_name = dir.get_next()
|
||||
if file_name == "":
|
||||
break
|
||||
elif (not file_name.begins_with(".")) && file_name.to_lower().ends_with("json") && not dir.current_is_dir():
|
||||
results.append(file_name)
|
||||
|
||||
dir.list_dir_end()
|
||||
return results
|
||||
|
||||
|
||||
# This returns an array of arrays, with priorities.
|
||||
# In particular, it takes an array of paths to look for
|
||||
# arrays in, in order of file and palette override priority
|
||||
# such that the files in the first directory override the
|
||||
# second, third, etc. ^.^
|
||||
# It returns an array of arrays, where each output array
|
||||
# corresponds to the given input array at the same index, and
|
||||
# contains the (relative to the given directory) palette files
|
||||
# to load, excluding all ones already existing in higher-priority
|
||||
# directories. nya
|
||||
# in particular, this also means you can run backwards on the result
|
||||
# so that palettes with the given palette name in the higher priority
|
||||
# directories override those set in lower priority directories :)
|
||||
func get_palette_priority_file_map(looking_paths: Array) -> Array:
|
||||
var final_list := []
|
||||
# Holds pattern files already found
|
||||
var working_file_set : Dictionary = {}
|
||||
for search_directory in looking_paths:
|
||||
var to_add_files := []
|
||||
var files = get_palette_files(search_directory)
|
||||
# files to check
|
||||
for maybe_to_add in files:
|
||||
if not maybe_to_add in working_file_set:
|
||||
to_add_files.append(maybe_to_add)
|
||||
working_file_set[maybe_to_add] = true
|
||||
|
||||
final_list.append(to_add_files)
|
||||
return final_list
|
||||
|
||||
|
||||
# Locate the highest priority palette by the given relative filename
|
||||
# If none is found in the directories, then do nothing and return
|
||||
# null
|
||||
func get_best_palette_file_location(looking_paths: Array, fname: String): # -> String:
|
||||
var priority_fmap : Array = get_palette_priority_file_map(looking_paths)
|
||||
for i in range(len(looking_paths)):
|
||||
var base_path : String = looking_paths[i]
|
||||
var the_files : Array = priority_fmap[i]
|
||||
if the_files.has(fname):
|
||||
return base_path.plus_file(fname)
|
||||
|
||||
return null
|
||||
|
||||
|
||||
func save_palette(palette_name : String, filename : String) -> void:
|
||||
Global.directory_module.ensure_xdg_user_dirs_exist()
|
||||
var palette = Global.palettes[palette_name]
|
||||
var palettes_write_path: String = Global.directory_module.get_palette_write_path()
|
||||
palette.save_to_file(palettes_write_path.plus_file(filename))
|
||||
|
||||
|
||||
func _on_NewPaletteDialog_popup_hide() -> void:
|
||||
Global.can_draw = true
|
13
src/Palette/PaletteImportFileDialog.tscn
Normal file
13
src/Palette/PaletteImportFileDialog.tscn
Normal file
|
@ -0,0 +1,13 @@
|
|||
[gd_scene format=2]
|
||||
|
||||
[node name="PaletteImportFileDialog" type="FileDialog"]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
rect_min_size = Vector2( 500, 300 )
|
||||
window_title = "Open a File"
|
||||
resizable = true
|
||||
mode = 0
|
||||
access = 2
|
||||
filters = PoolStringArray( "*.json ; JavaScript Object Notation" )
|
||||
current_dir = "D:/GitHub/Pixelorama"
|
||||
current_path = "D:/GitHub/Pixelorama/"
|
29
src/PatternButton.gd
Normal file
29
src/PatternButton.gd
Normal file
|
@ -0,0 +1,29 @@
|
|||
extends TextureButton
|
||||
|
||||
|
||||
var image : Image
|
||||
var image_size : Vector2
|
||||
var texture : ImageTexture
|
||||
|
||||
|
||||
func _ready():
|
||||
if image:
|
||||
image_size = image.get_size()
|
||||
texture = ImageTexture.new()
|
||||
texture.create_from_image(image, 0)
|
||||
|
||||
|
||||
func _on_PatternButton_pressed() -> void:
|
||||
if Global.pattern_window_position == "left":
|
||||
Global.pattern_left_image = image
|
||||
Global.left_fill_pattern_container.get_child(0).get_child(0).texture = texture
|
||||
Global.left_fill_pattern_container.get_child(2).get_child(1).max_value = image_size.x - 1
|
||||
Global.left_fill_pattern_container.get_child(3).get_child(1).max_value = image_size.y - 1
|
||||
|
||||
elif Global.pattern_window_position == "right":
|
||||
Global.pattern_right_image = image
|
||||
Global.right_fill_pattern_container.get_child(0).get_child(0).texture = texture
|
||||
Global.right_fill_pattern_container.get_child(2).get_child(1).max_value = image_size.x - 1
|
||||
Global.right_fill_pattern_container.get_child(3).get_child(1).max_value = image_size.y - 1
|
||||
|
||||
Global.patterns_popup.hide()
|
28
src/PatternButton.tscn
Normal file
28
src/PatternButton.tscn
Normal file
|
@ -0,0 +1,28 @@
|
|||
[gd_scene load_steps=3 format=2]
|
||||
|
||||
[ext_resource path="res://Assets/Graphics/Brush_button.png" type="Texture" id=1]
|
||||
[ext_resource path="res://src/PatternButton.gd" type="Script" id=2]
|
||||
|
||||
|
||||
[node name="PatternButton" type="TextureButton"]
|
||||
margin_right = 32.0
|
||||
margin_bottom = 32.0
|
||||
rect_min_size = Vector2( 3, 0 )
|
||||
button_mask = 7
|
||||
texture_normal = ExtResource( 1 )
|
||||
stretch_mode = 5
|
||||
script = ExtResource( 2 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="PatternTexture" type="TextureRect" parent="."]
|
||||
margin_right = 32.0
|
||||
margin_bottom = 32.0
|
||||
rect_min_size = Vector2( 32, 32 )
|
||||
expand = true
|
||||
stretch_mode = 6
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
[connection signal="pressed" from="." to="." method="_on_PatternButton_pressed"]
|
76
src/Rulers/Guides.gd
Normal file
76
src/Rulers/Guides.gd
Normal file
|
@ -0,0 +1,76 @@
|
|||
class_name Guide
|
||||
extends Line2D
|
||||
|
||||
enum Types {HORIZONTAL, VERTICAL}
|
||||
|
||||
var font := preload("res://Assets/Fonts/Roboto-Regular.tres")
|
||||
var has_focus := true
|
||||
var mouse_pos := Vector2.ZERO
|
||||
var previous_points := points
|
||||
var type = Types.HORIZONTAL
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
width = 0.1
|
||||
default_color = Global.guide_color
|
||||
|
||||
|
||||
func _input(_event : InputEvent):
|
||||
width = Global.camera.zoom.x * 2
|
||||
mouse_pos = get_local_mouse_position()
|
||||
var point0 := points[0]
|
||||
var point1 := points[1]
|
||||
if type == Types.HORIZONTAL:
|
||||
point0.y -= width * 3
|
||||
point1.y += width * 3
|
||||
else:
|
||||
point0.x -= width * 3
|
||||
point1.x += width * 3
|
||||
if Global.can_draw and Global.has_focus and point_in_rectangle(mouse_pos, point0, point1) and Input.is_action_just_pressed("left_mouse"):
|
||||
if !point_in_rectangle(Global.canvas.current_pixel, Global.canvas.location, Global.canvas.location + Global.canvas.size):
|
||||
has_focus = true
|
||||
Global.has_focus = false
|
||||
update()
|
||||
if has_focus:
|
||||
if Input.is_action_just_pressed("left_mouse"):
|
||||
previous_points = points
|
||||
if Input.is_action_pressed("left_mouse"):
|
||||
if type == Types.HORIZONTAL:
|
||||
points[0].y = round(mouse_pos.y)
|
||||
points[1].y = round(mouse_pos.y)
|
||||
else:
|
||||
points[0].x = round(mouse_pos.x)
|
||||
points[1].x = round(mouse_pos.x)
|
||||
if Input.is_action_just_released("left_mouse"):
|
||||
Global.has_focus = true
|
||||
has_focus = false
|
||||
if !outside_canvas():
|
||||
update()
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
if has_focus:
|
||||
var viewport_size: Vector2 = Global.main_viewport.rect_size
|
||||
var zoom: Vector2 = Global.camera.zoom
|
||||
if type == Types.HORIZONTAL:
|
||||
draw_set_transform(Vector2(Global.camera.offset.x - (viewport_size.x / 2) * zoom.x, points[0].y + font.get_height() * zoom.x * 2), rotation, zoom * 2)
|
||||
draw_string(font, Vector2.ZERO, "%spx" % str(round(mouse_pos.y)))
|
||||
else:
|
||||
draw_set_transform(Vector2(points[0].x + font.get_height() * zoom.y, Global.camera.offset.y - (viewport_size.y / 2.25) * zoom.y), rotation, zoom * 2)
|
||||
draw_string(font, Vector2.ZERO, "%spx" % str(round(mouse_pos.x)))
|
||||
|
||||
|
||||
func outside_canvas() -> bool:
|
||||
if type == Types.HORIZONTAL:
|
||||
if points[0].y < 0 || points[0].y > Global.canvas.size.y:
|
||||
queue_free()
|
||||
return true
|
||||
else:
|
||||
if points[0].x < 0 || points[0].x > Global.canvas.size.x:
|
||||
queue_free()
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
func point_in_rectangle(p : Vector2, coord1 : Vector2, coord2 : Vector2) -> bool:
|
||||
return p.x > coord1.x && p.y > coord1.y && p.x < coord2.x && p.y < coord2.y
|
79
src/Rulers/HorizontalRuler.gd
Normal file
79
src/Rulers/HorizontalRuler.gd
Normal file
|
@ -0,0 +1,79 @@
|
|||
extends Button
|
||||
|
||||
const RULER_WIDTH := 16
|
||||
|
||||
var font := preload("res://Assets/Fonts/Roboto-Small.tres")
|
||||
var major_subdivision := 2
|
||||
var minor_subdivision := 4
|
||||
|
||||
var first : Vector2
|
||||
var last : Vector2
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
Global.main_viewport.connect("item_rect_changed", self, "update")
|
||||
|
||||
|
||||
# Code taken and modified from Godot's source code
|
||||
func _draw() -> void:
|
||||
var transform := Transform2D()
|
||||
var ruler_transform := Transform2D()
|
||||
var major_subdivide := Transform2D()
|
||||
var minor_subdivide := Transform2D()
|
||||
var zoom: float = 1 / Global.camera.zoom.x
|
||||
transform.x = Vector2(zoom, zoom)
|
||||
|
||||
transform.origin = Global.main_viewport.rect_size / 2 + Global.camera.offset * -zoom
|
||||
|
||||
var basic_rule := 100.0
|
||||
var i := 0
|
||||
while(basic_rule * zoom > 100):
|
||||
basic_rule /= 5.0 if i % 2 else 2.0
|
||||
i += 1
|
||||
i = 0
|
||||
while(basic_rule * zoom < 100):
|
||||
basic_rule *= 2.0 if i % 2 else 5.0
|
||||
i += 1
|
||||
|
||||
ruler_transform = ruler_transform.scaled(Vector2(basic_rule, basic_rule))
|
||||
|
||||
major_subdivide = major_subdivide.scaled(Vector2(1.0 / major_subdivision, 1.0 / major_subdivision))
|
||||
minor_subdivide = minor_subdivide.scaled(Vector2(1.0 / minor_subdivision, 1.0 / minor_subdivision))
|
||||
|
||||
first = (transform * ruler_transform * major_subdivide * minor_subdivide).affine_inverse().xform(Vector2.ZERO)
|
||||
last = (transform * ruler_transform * major_subdivide * minor_subdivide).affine_inverse().xform(Global.main_viewport.rect_size)
|
||||
|
||||
for i in range(ceil(first.x), ceil(last.x)):
|
||||
var position : Vector2 = (transform * ruler_transform * major_subdivide * minor_subdivide).xform(Vector2(i, 0))
|
||||
if i % (major_subdivision * minor_subdivision) == 0:
|
||||
draw_line(Vector2(position.x + RULER_WIDTH, 0), Vector2(position.x + RULER_WIDTH, RULER_WIDTH), Color.white)
|
||||
var val = (ruler_transform * major_subdivide * minor_subdivide).xform(Vector2(i, 0)).x
|
||||
draw_string(font, Vector2(position.x + RULER_WIDTH + 2, font.get_height() - 4), str(int(val)))
|
||||
else:
|
||||
if i % minor_subdivision == 0:
|
||||
draw_line(Vector2(position.x + RULER_WIDTH, RULER_WIDTH * 0.33), Vector2(position.x + RULER_WIDTH, RULER_WIDTH), Color.white)
|
||||
else:
|
||||
draw_line(Vector2(position.x + RULER_WIDTH, RULER_WIDTH * 0.66), Vector2(position.x + RULER_WIDTH, RULER_WIDTH), Color.white)
|
||||
|
||||
|
||||
func _on_HorizontalRuler_pressed() -> void:
|
||||
if !Global.show_guides:
|
||||
return
|
||||
var mouse_pos := get_local_mouse_position()
|
||||
if mouse_pos.x < RULER_WIDTH: # For double guides
|
||||
Global.vertical_ruler._on_VerticalRuler_pressed()
|
||||
var guide := Guide.new()
|
||||
guide.type = guide.Types.HORIZONTAL
|
||||
guide.add_point(Vector2(-99999, Global.canvas.current_pixel.y))
|
||||
guide.add_point(Vector2(99999, Global.canvas.current_pixel.y))
|
||||
Global.canvas.add_child(guide)
|
||||
Global.has_focus = false
|
||||
update()
|
||||
|
||||
|
||||
func _on_HorizontalRuler_mouse_entered() -> void:
|
||||
var mouse_pos := get_local_mouse_position()
|
||||
if mouse_pos.x < RULER_WIDTH: # For double guides
|
||||
mouse_default_cursor_shape = Control.CURSOR_FDIAGSIZE
|
||||
else:
|
||||
mouse_default_cursor_shape = Control.CURSOR_VSPLIT
|
71
src/Rulers/VerticalRuler.gd
Normal file
71
src/Rulers/VerticalRuler.gd
Normal file
|
@ -0,0 +1,71 @@
|
|||
extends Button
|
||||
|
||||
const RULER_WIDTH := 16
|
||||
|
||||
var font := preload("res://Assets/Fonts/Roboto-Small.tres")
|
||||
var major_subdivision := 2
|
||||
var minor_subdivision := 4
|
||||
|
||||
var first : Vector2
|
||||
var last : Vector2
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
Global.main_viewport.connect("item_rect_changed", self, "update")
|
||||
|
||||
|
||||
# Code taken and modified from Godot's source code
|
||||
func _draw() -> void:
|
||||
var transform := Transform2D()
|
||||
var ruler_transform := Transform2D()
|
||||
var major_subdivide := Transform2D()
|
||||
var minor_subdivide := Transform2D()
|
||||
var zoom: float = 1 / Global.camera.zoom.x
|
||||
transform.y = Vector2(zoom, zoom)
|
||||
|
||||
transform.origin = Global.main_viewport.rect_size / 2 + Global.camera.offset * -zoom
|
||||
|
||||
var basic_rule := 100.0
|
||||
var i := 0
|
||||
while(basic_rule * zoom > 100):
|
||||
basic_rule /= 5.0 if i % 2 else 2.0
|
||||
i += 1
|
||||
i = 0
|
||||
while(basic_rule * zoom < 100):
|
||||
basic_rule *= 2.0 if i % 2 else 5.0
|
||||
i += 1
|
||||
|
||||
ruler_transform = ruler_transform.scaled(Vector2(basic_rule, basic_rule))
|
||||
|
||||
major_subdivide = major_subdivide.scaled(Vector2(1.0 / major_subdivision, 1.0 / major_subdivision))
|
||||
minor_subdivide = minor_subdivide.scaled(Vector2(1.0 / minor_subdivision, 1.0 / minor_subdivision))
|
||||
|
||||
first = (transform * ruler_transform * major_subdivide * minor_subdivide).affine_inverse().xform(Vector2.ZERO)
|
||||
last = (transform * ruler_transform * major_subdivide * minor_subdivide).affine_inverse().xform(Global.main_viewport.rect_size)
|
||||
|
||||
for i in range(ceil(first.y), ceil(last.y)):
|
||||
var position : Vector2 = (transform * ruler_transform * major_subdivide * minor_subdivide).xform(Vector2(0, i))
|
||||
if i % (major_subdivision * minor_subdivision) == 0:
|
||||
draw_line(Vector2(0, position.y), Vector2(RULER_WIDTH, position.y), Color.white)
|
||||
var text_xform = Transform2D(-PI / 2, Vector2(font.get_height() - 4, position.y - 2))
|
||||
draw_set_transform_matrix(get_transform() * text_xform)
|
||||
var val = (ruler_transform * major_subdivide * minor_subdivide).xform(Vector2(0, i)).y
|
||||
draw_string(font, Vector2(), str(int(val)))
|
||||
draw_set_transform_matrix(get_transform())
|
||||
else:
|
||||
if i % minor_subdivision == 0:
|
||||
draw_line(Vector2(RULER_WIDTH * 0.33, position.y), Vector2(RULER_WIDTH, position.y), Color.white)
|
||||
else:
|
||||
draw_line(Vector2(RULER_WIDTH * 0.66, position.y), Vector2(RULER_WIDTH, position.y), Color.white)
|
||||
|
||||
|
||||
func _on_VerticalRuler_pressed() -> void:
|
||||
if !Global.show_guides:
|
||||
return
|
||||
var guide := Guide.new()
|
||||
guide.type = guide.Types.VERTICAL
|
||||
guide.add_point(Vector2(Global.canvas.current_pixel.x, -99999))
|
||||
guide.add_point(Vector2(Global.canvas.current_pixel.x, 99999))
|
||||
Global.canvas.add_child(guide)
|
||||
Global.has_focus = false
|
||||
update()
|
6
src/SecondViewport.gd
Normal file
6
src/SecondViewport.gd
Normal file
|
@ -0,0 +1,6 @@
|
|||
extends Viewport
|
||||
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready() -> void:
|
||||
world_2d = Global.canvas.get_parent().world_2d
|
149
src/SelectionRectangle.gd
Normal file
149
src/SelectionRectangle.gd
Normal file
|
@ -0,0 +1,149 @@
|
|||
extends Polygon2D
|
||||
|
||||
var img : Image
|
||||
var tex : ImageTexture
|
||||
var is_dragging := false
|
||||
var move_pixels := false
|
||||
var diff_x := 0.0
|
||||
var diff_y := 0.0
|
||||
var orig_x := 0.0
|
||||
var orig_y := 0.0
|
||||
var orig_colors := []
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
img = Image.new()
|
||||
img.create(1, 1, false, Image.FORMAT_RGBA8)
|
||||
img.lock()
|
||||
tex = ImageTexture.new()
|
||||
tex.create_from_image(img, 0)
|
||||
|
||||
|
||||
func _process(_delta : float) -> void:
|
||||
if Global.layers[Global.current_layer][2]:
|
||||
return
|
||||
var mouse_pos: Vector2 = get_local_mouse_position() - Global.canvas.location
|
||||
var mouse_pos_floored := mouse_pos.floor()
|
||||
var start_pos := polygon[0]
|
||||
var end_pos := polygon[2]
|
||||
var current_layer_index : int = Global.current_layer
|
||||
var layer : Image = Global.canvas.layers[current_layer_index][0]
|
||||
|
||||
if end_pos == start_pos:
|
||||
visible = false
|
||||
else:
|
||||
visible = true
|
||||
|
||||
if Global.can_draw and Global.has_focus and point_in_rectangle(mouse_pos, polygon[0], polygon[2]) and Global.selected_pixels.size() > 0 and (Global.current_left_tool == "RectSelect" or Global.current_right_tool == "RectSelect"):
|
||||
get_parent().get_parent().mouse_default_cursor_shape = Input.CURSOR_MOVE
|
||||
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
|
||||
if (Global.current_left_tool == "RectSelect" && Input.is_action_just_pressed("left_mouse")) || (Global.current_right_tool == "RectSelect" && Input.is_action_just_pressed("right_mouse")):
|
||||
# Begin dragging
|
||||
is_dragging = true
|
||||
if Input.is_key_pressed(KEY_SHIFT):
|
||||
move_pixels = true
|
||||
else:
|
||||
move_pixels = false
|
||||
img.fill(Color(0, 0, 0, 0))
|
||||
diff_x = end_pos.x - mouse_pos_floored.x
|
||||
diff_y = end_pos.y - mouse_pos_floored.y
|
||||
orig_x = start_pos.x - mouse_pos_floored.x
|
||||
orig_y = start_pos.y - mouse_pos_floored.y
|
||||
if move_pixels:
|
||||
img.unlock()
|
||||
img.resize(polygon[2].x - polygon[0].x, polygon[2].y - polygon[0].y, 0)
|
||||
img.lock()
|
||||
for i in range(Global.selected_pixels.size()):
|
||||
var curr_px = Global.selected_pixels[i]
|
||||
if point_in_rectangle(curr_px, Global.canvas.location - Vector2.ONE, Global.canvas.size):
|
||||
orig_colors.append(layer.get_pixelv(curr_px)) # Color of pixel
|
||||
var px = curr_px - Global.selected_pixels[0]
|
||||
img.set_pixelv(px, orig_colors[i])
|
||||
layer.set_pixelv(curr_px, Color(0, 0, 0, 0))
|
||||
else: # If part of selection is outside canvas
|
||||
orig_colors.append(Color(0, 0, 0, 0))
|
||||
Global.canvas.update_texture(current_layer_index)
|
||||
tex.create_from_image(img, 0)
|
||||
update()
|
||||
|
||||
# Makes line2d invisible
|
||||
Global.canvas.line_2d.default_color = Color(0, 0, 0, 0)
|
||||
else:
|
||||
get_parent().get_parent().mouse_default_cursor_shape = Input.CURSOR_ARROW
|
||||
|
||||
if is_dragging:
|
||||
if (Global.current_left_tool == "RectSelect" && Input.is_action_pressed("left_mouse")) || (Global.current_right_tool == "RectSelect" && Input.is_action_pressed("right_mouse")):
|
||||
# Drag
|
||||
start_pos.x = orig_x + mouse_pos_floored.x
|
||||
end_pos.x = diff_x + mouse_pos_floored.x
|
||||
|
||||
start_pos.y = orig_y + mouse_pos_floored.y
|
||||
end_pos.y = diff_y + mouse_pos_floored.y
|
||||
polygon[0] = start_pos
|
||||
polygon[1] = Vector2(end_pos.x, start_pos.y)
|
||||
polygon[2] = end_pos
|
||||
polygon[3] = Vector2(start_pos.x, end_pos.y)
|
||||
|
||||
if (Global.current_left_tool == "RectSelect" && Input.is_action_just_released("left_mouse")) || (Global.current_right_tool == "RectSelect" && Input.is_action_just_released("right_mouse")):
|
||||
# Release Drag
|
||||
is_dragging = false
|
||||
if move_pixels:
|
||||
for i in range(orig_colors.size()):
|
||||
if orig_colors[i].a > 0:
|
||||
var px = polygon[0] + Global.selected_pixels[i] - Global.selected_pixels[0]
|
||||
if point_in_rectangle(px, Global.canvas.location - Vector2.ONE, Global.canvas.size):
|
||||
layer.set_pixelv(px, orig_colors[i])
|
||||
Global.canvas.update_texture(current_layer_index)
|
||||
img.fill(Color(0, 0, 0, 0))
|
||||
tex.create_from_image(img, 0)
|
||||
update()
|
||||
|
||||
orig_colors.clear()
|
||||
Global.selected_pixels.clear()
|
||||
for xx in range(start_pos.x, end_pos.x):
|
||||
for yy in range(start_pos.y, end_pos.y):
|
||||
Global.selected_pixels.append(Vector2(xx, yy))
|
||||
|
||||
Global.canvas.handle_redo("Rectangle Select") # Redo
|
||||
|
||||
# Makes line2d visible
|
||||
Global.canvas.line_2d.default_color = Color.darkgray
|
||||
|
||||
if Global.selected_pixels.size() > 0:
|
||||
# Handle copy
|
||||
if Input.is_action_just_pressed("copy"):
|
||||
# Save as custom brush
|
||||
var brush_img := Image.new()
|
||||
brush_img = layer.get_rect(Rect2(polygon[0], polygon[2] - polygon[0]))
|
||||
if brush_img.is_invisible():
|
||||
return
|
||||
brush_img = brush_img.get_rect(brush_img.get_used_rect()) # Save only the visible pixels
|
||||
Global.custom_brushes.append(brush_img)
|
||||
Global.create_brush_button(brush_img)
|
||||
|
||||
# Have it in the clipboard so it can be pasted later
|
||||
Global.image_clipboard = layer.get_rect(Rect2(polygon[0], polygon[2] - polygon[0]))
|
||||
|
||||
# Handle paste
|
||||
if Input.is_action_just_pressed("paste") && Global.image_clipboard.get_size() > Vector2.ZERO:
|
||||
Global.canvas.handle_undo("Draw")
|
||||
layer.blend_rect(Global.image_clipboard, Rect2(Vector2.ZERO, polygon[2]-polygon[0]), polygon[0])
|
||||
layer.lock()
|
||||
Global.canvas.handle_redo("Draw")
|
||||
|
||||
if Input.is_action_just_pressed("delete"):
|
||||
Global.canvas.handle_undo("Draw")
|
||||
for xx in range(start_pos.x, end_pos.x):
|
||||
for yy in range(start_pos.y, end_pos.y):
|
||||
if point_in_rectangle(Vector2(xx, yy), Global.canvas.location - Vector2.ONE, Global.canvas.location + Global.canvas.size):
|
||||
layer.set_pixel(xx, yy, Color(0, 0, 0, 0))
|
||||
Global.canvas.handle_redo("Draw")
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
if img.get_size() == polygon[2] - polygon[0]:
|
||||
draw_texture(tex, polygon[0], Color(1, 1, 1, 0.5))
|
||||
|
||||
|
||||
func point_in_rectangle(p : Vector2, coord1 : Vector2, coord2 : Vector2) -> bool:
|
||||
return p.x > coord1.x && p.y > coord1.y && p.x < coord2.x && p.y < coord2.y
|
8
src/TransparentChecker.gd
Normal file
8
src/TransparentChecker.gd
Normal file
|
@ -0,0 +1,8 @@
|
|||
extends ColorRect
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
rect_size = Global.canvas.size
|
||||
material.set_shader_param("size", Global.checker_size)
|
||||
material.set_shader_param("color1", Global.checker_color_1)
|
||||
material.set_shader_param("color2", Global.checker_color_2)
|
143
src/XDGDataPaths.gd
Normal file
143
src/XDGDataPaths.gd
Normal file
|
@ -0,0 +1,143 @@
|
|||
extends Node
|
||||
|
||||
# These are *with* the config subdirectory name
|
||||
var xdg_data_home : String
|
||||
var xdg_data_dirs : Array
|
||||
|
||||
# These are *without* the config subdirectory name
|
||||
var raw_xdg_data_home : String
|
||||
var raw_xdg_data_dirs : Array
|
||||
|
||||
# Default location for xdg_data_home relative to $HOME
|
||||
const default_xdg_data_home_rel := ".local/share"
|
||||
const default_xdg_data_dirs := ["/usr/local/share", "/usr/share"]
|
||||
|
||||
const config_subdir_name := "pixelorama"
|
||||
|
||||
const palettes_data_subdirectory := "Palettes"
|
||||
const brushes_data_subdirectory := "Brushes"
|
||||
const patterns_data_subdirectory := "Patterns"
|
||||
|
||||
|
||||
# Get if we should use XDG standard or not nyaaaa
|
||||
func use_xdg_standard() -> bool:
|
||||
# see: https://docs.godotengine.org/en/latest/getting_started/workflow/export/feature_tags.html
|
||||
# return OS.has_feature("Linux") or OS.has_feature("BSD")
|
||||
# Previous was unreliable and buggy >.< nyaa
|
||||
return OS.get_name() == "X11"
|
||||
|
||||
|
||||
func _init():
|
||||
if use_xdg_standard():
|
||||
print("Detected system where we should use XDG basedir standard (currently Linux or BSD)")
|
||||
var home := OS.get_environment("HOME")
|
||||
raw_xdg_data_home = home.plus_file(
|
||||
default_xdg_data_home_rel
|
||||
)
|
||||
xdg_data_home = raw_xdg_data_home.plus_file(
|
||||
config_subdir_name
|
||||
)
|
||||
|
||||
# Create defaults
|
||||
xdg_data_dirs = []
|
||||
raw_xdg_data_dirs = default_xdg_data_dirs
|
||||
for default_loc in raw_xdg_data_dirs:
|
||||
xdg_data_dirs.append(
|
||||
default_loc.plus_file(config_subdir_name)
|
||||
)
|
||||
|
||||
# Now check the XDG environment variables and if
|
||||
# present, replace the defaults with them!
|
||||
# See: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||
# Checks the xdg data home var
|
||||
if OS.has_environment("XDG_DATA_HOME"):
|
||||
raw_xdg_data_home = OS.get_environment("XDG_DATA_HOME")
|
||||
xdg_data_home = raw_xdg_data_home.plus_file(config_subdir_name)
|
||||
# Checks the list of files var, and processes them.
|
||||
if OS.has_environment("XDG_DATA_DIRS"):
|
||||
var raw_env_var := OS.get_environment("XDG_DATA_DIRS")
|
||||
# includes empties.
|
||||
var unappended_subdirs := raw_env_var.split(":", true)
|
||||
raw_xdg_data_dirs = unappended_subdirs
|
||||
xdg_data_dirs = []
|
||||
for unapp_subdir in raw_xdg_data_dirs:
|
||||
xdg_data_dirs.append(unapp_subdir.plus_file(config_subdir_name))
|
||||
|
||||
else:
|
||||
raw_xdg_data_home = Global.root_directory
|
||||
xdg_data_home = raw_xdg_data_home.plus_file(config_subdir_name)
|
||||
raw_xdg_data_dirs = []
|
||||
xdg_data_dirs = []
|
||||
|
||||
|
||||
func append_file_to_all(basepaths: Array, subpath: String) -> Array:
|
||||
var res := []
|
||||
for _path in basepaths:
|
||||
res.append(_path.plus_file(subpath))
|
||||
return res
|
||||
|
||||
|
||||
# Get search paths in order of priority
|
||||
func get_search_paths_in_order() -> Array:
|
||||
return [xdg_data_home] + xdg_data_dirs
|
||||
|
||||
|
||||
# Gets the paths, in order of search priority, for palettes.
|
||||
func get_palette_search_path_in_order() -> Array:
|
||||
var base_paths := get_search_paths_in_order()
|
||||
return append_file_to_all(base_paths, palettes_data_subdirectory)
|
||||
|
||||
|
||||
# Gets the paths, in order of search priority, for brushes.
|
||||
func get_brushes_search_path_in_order() -> Array:
|
||||
var base_paths := get_search_paths_in_order()
|
||||
return append_file_to_all(base_paths, brushes_data_subdirectory)
|
||||
|
||||
|
||||
# Gets the paths, in order of search priority, for patterns.
|
||||
func get_patterns_search_path_in_order() -> Array:
|
||||
var base_paths := get_search_paths_in_order()
|
||||
return append_file_to_all(base_paths, patterns_data_subdirectory)
|
||||
|
||||
|
||||
# Get the path that we are ok to be writing palettes to:
|
||||
func get_palette_write_path() -> String:
|
||||
return xdg_data_home.plus_file(palettes_data_subdirectory)
|
||||
|
||||
|
||||
# Get the path that we are ok to be writing brushes to:
|
||||
func get_brushes_write_path() -> String:
|
||||
return xdg_data_home.plus_file(brushes_data_subdirectory)
|
||||
|
||||
|
||||
# Get the path that we are ok to be writing patterns to:
|
||||
func get_patterns_write_path() -> String:
|
||||
return xdg_data_home.plus_file(patterns_data_subdirectory)
|
||||
|
||||
|
||||
# Ensure the user xdg directories exist:
|
||||
func ensure_xdg_user_dirs_exist() -> void:
|
||||
if !OS.has_feature("standalone"): # Don't execute if we're in the editor
|
||||
return
|
||||
|
||||
var base_dir := Directory.new()
|
||||
base_dir.open(raw_xdg_data_home)
|
||||
# Ensure the main config directory exists.
|
||||
if not base_dir.dir_exists(xdg_data_home):
|
||||
base_dir.make_dir(xdg_data_home)
|
||||
|
||||
var actual_data_dir := Directory.new()
|
||||
actual_data_dir.open(xdg_data_home)
|
||||
var palette_writing_dir := get_palette_write_path()
|
||||
var brushes_writing_dir := get_brushes_write_path()
|
||||
var pattern_writing_dir := get_patterns_write_path()
|
||||
# Create the palette and brush dirs
|
||||
if not actual_data_dir.dir_exists(palette_writing_dir):
|
||||
print("Making directory %s" % [palette_writing_dir])
|
||||
actual_data_dir.make_dir(palette_writing_dir)
|
||||
if not actual_data_dir.dir_exists(brushes_writing_dir):
|
||||
print("Making directory %s" % [brushes_writing_dir])
|
||||
actual_data_dir.make_dir(brushes_writing_dir)
|
||||
if not actual_data_dir.dir_exists(pattern_writing_dir):
|
||||
print("Making directory %s" % [pattern_writing_dir])
|
||||
actual_data_dir.make_dir(pattern_writing_dir)
|
Loading…
Add table
Add a link
Reference in a new issue