diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5c0ffd85d..a2326b7a0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,6 +8,7 @@ * [Communicating with developers](#communicating-with-developers) **Please read the first section before reporting a bug!** +

## Reporting bugs or proposing features Please, open just one issue for each bug you'd like to report, or a feature you'd like to request. Don't open many issues for the same bug or feature request, and don't use the same issue to report more than one bugs, or to request more than one feature. It's best to open different issues for each bug/feature request. @@ -15,7 +16,7 @@ Please, open just one issue for each bug you'd like to report, or a feature you' Also, make sure to search the [issue tracker](https://github.com/Orama-Interactive/Pixelorama/issues) before opening a new issue, in case an issue like that exists. If you're unsure, feel free to open an issue. If it's a duplicate, we'll handle it. When reporting a bug, make sure to provide enough details, such as information about your Operating System (OS), Pixelorama version, Godot version (if you're using the source project) and clear steps to reproduce the issue. Feel free to include screenshots that might help, too. - +

## Contributing pull requests If you want to add new features or fix bugs, please make sure that: @@ -37,12 +38,12 @@ Please create different pull requests for each feature you'd like to implement, This [Git style guide](https://github.com/agis-/git-style-guide) has some good practices to have in mind. Keep in mind, however, that not all PRs will be merged. Some may need discussion, or others may be downright closed. - +

## Contributing translations Pixelorama uses [Crowdin](https://crowdin.com/project/pixelorama) to host the translations. In order to contribute, you need to login with your Crowdin account, select the language(s) you'd like to provide translations for, select `Translations.pot` and start translating! If you need help with the context of some strings, or want to translate in a language that is not available, feel free to contact me (Overloaded). All languages are welcome to be translated! - +

## Communicating with developers To communicate with developers (e.g. to discuss a feature you want to implement or a bug you want to fix), the following channels can be used: diff --git a/Changelog.md b/Changelog.md index 24a5780de..8fdb9d211 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +

## [v0.7] - Unreleased This update has been brought to you by the contributions of: @@ -65,7 +66,7 @@ sapient-cogbag, Kinwailo, Igor Santarek (jegor377), Dávid Gábor BODOR (dragonf ### Removed - It's no longer possible for frames to have different amounts of layers. All frames have the same amount. - The guides no longer work with undo/redo. - +

## [v0.6.2] - 17-02-2020 @@ -103,7 +104,7 @@ sapient-cogbag, Kinwailo, Igor Santarek (jegor377), Dávid Gábor BODOR (dragonf - 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 and moving selection content were not working on Godot 3.2 - Issues #161 and #162 - +

## [v0.6.1] - 13-01-2020 @@ -134,7 +135,7 @@ sapient-cogbag, Kinwailo, Igor Santarek (jegor377), Dávid Gábor BODOR (dragonf - The canvas updates automatically when onion skinning settings change. - Fixed a rare crash with straight lines. It was possible that the variable `is_making_line` could be true, even if the line itself has been freed from memory. - Fixed issue where undo/redo was not working properly for straight lines that went outside the canvas. - +

## [v0.6] - 06-01-2020 diff --git a/Scripts/AnimationTimeline.gd b/Scripts/AnimationTimeline.gd index 70917960c..7b39190b9 100644 --- a/Scripts/AnimationTimeline.gd +++ b/Scripts/AnimationTimeline.gd @@ -9,6 +9,7 @@ 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 @@ -175,11 +176,14 @@ func _on_FrameTagButton_pressed() -> void: 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" % Global.theme_type) + 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" % Global.theme_type) + texture_button.texture = load("res://Assets/Graphics/%s Themes/Timeline/onion_skinning_off.png" % theme_type) func _on_OnionSkinningSettings_pressed() -> void: @@ -318,22 +322,26 @@ func _on_FirstFrame_pressed() -> void: Global.current_frame = 0 -func _on_FPSValue_value_changed(value) -> void: +func _on_FPSValue_value_changed(value : float) -> void: fps = float(value) Global.animation_timer.wait_time = 1 / fps -func _on_PastOnionSkinning_value_changed(value) -> void: + +func _on_PastOnionSkinning_value_changed(value : float) -> void: Global.onion_skinning_past_rate = int(value) Global.canvas.update() -func _on_FutureOnionSkinning_value_changed(value) -> void: + +func _on_FutureOnionSkinning_value_changed(value : float) -> void: Global.onion_skinning_future_rate = int(value) Global.canvas.update() -func _on_BlueRedMode_toggled(button_pressed) -> void: + +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: @@ -376,6 +384,7 @@ func add_layer(is_new := true) -> void: 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) diff --git a/Scripts/BrushButton.gd b/Scripts/BrushButton.gd index cfe5db4b4..edddc7532 100644 --- a/Scripts/BrushButton.gd +++ b/Scripts/BrushButton.gd @@ -1,10 +1,12 @@ extends BaseButton + +signal brush_selected + export var brush_type = 0 # Global.Brush_Types.PIXEL export var custom_brush_index := -3 -# warning-ignore:unused_class_variable var random_brushes := [] -signal brush_selected + func _on_BrushButton_pressed() -> void: # Delete the brush on middle mouse press @@ -59,6 +61,7 @@ func _on_BrushButton_pressed() -> void: 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: @@ -94,10 +97,12 @@ func _on_DeleteButton_pressed() -> void: 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 diff --git a/Scripts/CameraMovement.gd b/Scripts/CameraMovement.gd index 77a57fe1e..9cb8d9717 100644 --- a/Scripts/CameraMovement.gd +++ b/Scripts/CameraMovement.gd @@ -32,6 +32,7 @@ func dir_move_zoom_multiplier(press_time : float) -> float: else: return 0.0 + func reset_dir_move_time(direction) -> void: Global.key_move_press_time[direction] = 0.0 @@ -78,6 +79,7 @@ const directional_sign_multipliers := [ Vector2(1.0, 0.0) ] + # Process an action event for a pressed direction # action func process_direction_action_pressed(event: InputEvent) -> void: diff --git a/Scripts/Canvas.gd b/Scripts/Canvas.gd index d1791d7ba..f152707df 100644 --- a/Scripts/Canvas.gd +++ b/Scripts/Canvas.gd @@ -1,6 +1,11 @@ class_name Canvas extends Node2D + +const Drawer = preload("Drawers.gd").Drawer +const SimpleDrawer = preload("Drawers.gd").SimpleDrawer +const PixelPerfectDrawer = preload("Drawers.gd").PixelPerfectDrawer + var layers := [] var current_layer_index := 0 var location := Vector2.ZERO @@ -27,10 +32,6 @@ var is_making_selection := "None" var line_2d : Line2D var pen_pressure := 1.0 # For tablet pressure sensitivity -const Drawer = preload("Drawers.gd").Drawer -const SimpleDrawer = preload("Drawers.gd").SimpleDrawer -const PixelPerfectDrawer = preload("Drawers.gd").PixelPerfectDrawer - var pixel_perfect_drawer := PixelPerfectDrawer.new() var pixel_perfect_drawer_h_mirror := PixelPerfectDrawer.new() var pixel_perfect_drawer_v_mirror := PixelPerfectDrawer.new() @@ -82,6 +83,7 @@ func _ready() -> void: line_2d.add_point(previous_mouse_pos_for_lines) add_child(line_2d) + func _draw() -> void: # Onion Skinning if Global.onion_skinning: @@ -519,6 +521,7 @@ func _input(event : InputEvent) -> void: if sprite_changed_this_frame: update_texture(Global.current_layer) + func camera_zoom() -> void: # Set camera zoom based on the sprite size var bigger_canvas_axis = max(size.x, size.y) @@ -548,6 +551,7 @@ func camera_zoom() -> void: Global.transparent_checker._ready() # To update the rect size + func handle_undo(action : String) -> void: if !can_undo: return @@ -575,6 +579,7 @@ func handle_undo(action : String) -> void: can_undo = false + func handle_redo(action : String) -> void: can_undo = true @@ -595,6 +600,7 @@ func handle_redo(action : String) -> void: Global.undo_redo.add_do_method(Global, "redo", canvases, layer_index) Global.undo_redo.commit_action() + func update_texture(layer_index : int) -> void: layers[layer_index][1].create_from_image(layers[layer_index][0], 0) @@ -602,6 +608,7 @@ func update_texture(layer_index : int) -> void: frame_texture_rect = Global.find_node_by_name(Global.layers[layer_index][3].get_child(frame),"FrameTexture") frame_texture_rect.texture = layers[layer_index][1] + func pencil_and_eraser(sprite : Image, mouse_pos : Vector2, color : Color, current_mouse_button : String, current_action := "None") -> void: if made_line: return @@ -619,6 +626,7 @@ func pencil_and_eraser(sprite : Image, mouse_pos : Vector2, color : Color, curre elif point_in_rectangle(previous_mouse_pos, location, location + size): fill_gaps(sprite, mouse_pos, previous_mouse_pos, color, current_mouse_button, current_action) + func draw_brush(sprite : Image, pos : Vector2, color : Color, current_mouse_button : String, current_action := "None") -> void: if Global.can_draw && Global.has_focus: var brush_size := 1 @@ -855,6 +863,7 @@ func draw_brush(sprite : Image, pos : Vector2, color : Color, current_mouse_butt if is_making_line: line_2d.set_point_position(0, previous_mouse_pos_for_lines) + # Bresenham's Algorithm # Thanks to https://godotengine.org/qa/35276/tile-based-line-drawing-algorithm-efficiency func fill_gaps(sprite : Image, mouse_pos : Vector2, prev_mouse_pos : Vector2, color : Color, current_mouse_button : String, current_action := "None") -> void: @@ -880,6 +889,7 @@ func fill_gaps(sprite : Image, mouse_pos : Vector2, prev_mouse_pos : Vector2, co err += dx y += sy + # Thanks to https://en.wikipedia.org/wiki/Flood_fill func flood_fill(sprite : Image, pos : Vector2, target_color : Color, replace_color : Color) -> void: pos = pos.floor() @@ -986,6 +996,7 @@ func plot_circle(sprite : Image, xm : int, ym : int, r : int, color : Color, fil var draw_pos := Vector2(i + xm, j + ym) draw_pixel_blended(sprite, draw_pos, color) + func draw_pixel_blended(sprite : Image, pos : Vector2, color : Color) -> void: var saved_pixel_index := mouse_press_pixels.find(pos) if point_in_rectangle(pos, Vector2(west_limit - 1, north_limit - 1), Vector2(east_limit, south_limit)) && (saved_pixel_index == -1 || pen_pressure > mouse_press_pressure_values[saved_pixel_index]): @@ -999,6 +1010,7 @@ func draw_pixel_blended(sprite : Image, pos : Vector2, color : Color) -> void: mouse_press_pressure_values[saved_pixel_index] = pen_pressure sprite.set_pixelv(pos, color) + func blend_colors(color_1 : Color, color_2 : Color) -> Color: var color := Color() color.a = color_1.a + color_2.a * (1 - color_1.a) # Blend alpha @@ -1009,14 +1021,17 @@ func blend_colors(color_1 : Color, color_2 : Color) -> Color: color.b = (color_1.b * color_1.a + color_2.b * color_2.a * (1-color_1.a)) / color.a return color + # Checks if a point is inside a rectangle 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 + # Returns the position in the middle of a rectangle func rectangle_center(rect_position : Vector2, rect_size : Vector2) -> Vector2: return (rect_position - rect_size / 2).floor() + # Custom blend rect function, needed because Godot's issue #31124 func blend_rect(bg : Image, brush : Image, src_rect : Rect2, dst : Vector2) -> void: var brush_size := brush.get_size() @@ -1042,6 +1057,7 @@ func blend_rect(bg : Image, brush : Image, src_rect : Rect2, dst : Vector2) -> v bg.set_pixel(dst_x, dst_y, out_color) brush.unlock() + func adjust_hsv(img: Image, id : int, delta : float) -> void: var layer : Image = img layer.lock() diff --git a/Scripts/Dialogs/AboutDialog.gd b/Scripts/Dialogs/AboutDialog.gd index 0e561e587..7cc62ee48 100644 --- a/Scripts/Dialogs/AboutDialog.gd +++ b/Scripts/Dialogs/AboutDialog.gd @@ -12,6 +12,7 @@ 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)") @@ -37,6 +38,7 @@ func _ready() -> void: 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 @@ -86,10 +88,12 @@ func _on_AboutDialog_about_to_show() -> void: 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: @@ -109,8 +113,10 @@ func _on_Groups_item_selected() -> void: 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") diff --git a/Scripts/Dialogs/CreateNewImage.gd b/Scripts/Dialogs/CreateNewImage.gd index 347cd791f..670dc4b37 100644 --- a/Scripts/Dialogs/CreateNewImage.gd +++ b/Scripts/Dialogs/CreateNewImage.gd @@ -51,13 +51,15 @@ var TStrings ={ 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(): + +func _CreateOptionList() -> void: for i in Templates.values(): if i > 0: if TStrings[i] != "": @@ -65,6 +67,7 @@ func _CreateOptionList(): 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 @@ -89,6 +92,7 @@ func _on_CreateNewImage_confirmed() -> void: 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 @@ -99,10 +103,10 @@ func _on_CreateNewImage_about_to_show() -> void: 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 -# warning-ignore:unused_argument -func _on_RatioCheckBox_toggled(button_pressed: bool) -> void: +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"): @@ -110,12 +114,14 @@ func _on_RatioCheckBox_toggled(button_pressed: bool) -> void: 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] diff --git a/Scripts/Dialogs/ExportDialog.gd b/Scripts/Dialogs/ExportDialog.gd index a35afe5c5..3bf368016 100644 --- a/Scripts/Dialogs/ExportDialog.gd +++ b/Scripts/Dialogs/ExportDialog.gd @@ -1,7 +1,7 @@ extends AcceptDialog -enum ExportTab { Frame = 0, Spritesheet = 1, Animation = 2 } -var current_tab : int = ExportTab.Frame +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[] @@ -10,17 +10,17 @@ var processed_images = [] # Image[] var frame_number := 0 # Spritesheet options -enum Orientation { Rows = 0, Columns = 1 } -var orientation : int = Orientation.Rows +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 { MultipleFiles = 0, Animated = 1 } -var animation_type : int = AnimationType.MultipleFiles +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, PingPong = 2 } -var direction : int = AnimationDirection.Forward +enum AnimationDirection { FORWARD = 0, BACKWARDS = 1, PING_PONG = 2 } +var direction : int = AnimationDirection.FORWARD # Options var resize := 100 @@ -57,6 +57,7 @@ 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") @@ -70,16 +71,17 @@ func _ready() -> void: # Disable GIF export for unsupported platforms if not $GifExporter.is_platform_supported(): - $VBoxContainer/AnimationOptions/AnimationType.selected = AnimationType.MultipleFiles + $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: + ExportTab.FRAME: file_format = FileFormat.PNG $VBoxContainer/File/FileFormat.selected = FileFormat.PNG $FrameTimer.stop() @@ -89,12 +91,12 @@ func show_tab() -> void: $VBoxContainer/FrameOptions/FrameNumber/FrameNumber.value = frame_number process_frame() $VBoxContainer/FrameOptions.show() - ExportTab.Spritesheet: + ExportTab.SPRITESHEET: file_format = FileFormat.PNG $VBoxContainer/File/FileFormat.selected = FileFormat.PNG $FrameTimer.stop() if not was_exported: - orientation = Orientation.Rows + 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() @@ -102,7 +104,7 @@ func show_tab() -> void: $VBoxContainer/SpritesheetOptions/Orientation/LinesCountLabel.text = "Columns:" process_spritesheet() $VBoxContainer/SpritesheetOptions.show() - ExportTab.Animation: + ExportTab.ANIMATION: set_file_format_selector() process_animation() $VBoxContainer/AnimationOptions/AnimationType.selected = animation_type @@ -116,11 +118,11 @@ func show_tab() -> void: func external_export() -> void: restore_previous_export_settings() match current_tab: - ExportTab.Frame: + ExportTab.FRAME: process_frame() - ExportTab.Spritesheet: + ExportTab.SPRITESHEET: process_spritesheet() - ExportTab.Animation: + ExportTab.ANIMATION: process_animation() export_processed_images(true) @@ -136,8 +138,8 @@ func process_frame() -> void: 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 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 @@ -149,7 +151,7 @@ func process_spritesheet() -> void: var hh := 0 var vv := 0 for canvas in Global.canvases: - if orientation == Orientation.Rows: + if orientation == Orientation.ROWS: if vv < spritesheet_columns: origin.x = canvas.size.x * vv vv += 1 @@ -184,16 +186,16 @@ func process_animation() -> void: func set_preview() -> void: remove_previews() - if processed_images.size() == 1 and current_tab != ExportTab.Animation: + 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.MultipleFiles: + 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: + AnimationType.ANIMATED: $VBoxContainer/PreviewScroll/Previews.columns = 1 add_animated_preview() @@ -215,7 +217,7 @@ func add_image_preview(image: Image, canvas_number: int = -1) -> void: func add_animated_preview() -> void: - animated_preview_current_frame = processed_images.size() - 1 if direction == AnimationDirection.Backwards else 0 + animated_preview_current_frame = processed_images.size() - 1 if direction == AnimationDirection.BACKWARDS else 0 animated_preview_frames = [] for processed_image in processed_images: @@ -278,7 +280,7 @@ func export_processed_images(ignore_overwrites : bool) -> void: 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.MultipleFiles) else 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: @@ -301,24 +303,24 @@ func export_processed_images(ignore_overwrites : bool) -> void: 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: + 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: + 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: + AnimationDirection.FORWARD: for i in range(processed_images.size()): $GifExporter.write_frame(processed_images[i], background_color, frame_delay_in_ms) - AnimationDirection.Backwards: + 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.PingPong: + 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): @@ -410,19 +412,20 @@ func file_format_string(format_enum : int) -> String: func set_file_format_selector() -> void: $VBoxContainer/AnimationOptions/MultipleAnimationsDirectories.visible = false match animation_type: - AnimationType.MultipleFiles: + 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: + 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 @@ -437,6 +440,7 @@ func store_export_settings() -> void: 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 @@ -475,6 +479,7 @@ func _on_ExportDialog_about_to_show() -> void: 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() @@ -488,7 +493,7 @@ func _on_Frame_value_changed(value: float) -> void: func _on_Orientation_item_selected(id : int) -> void: orientation = id - if orientation == Orientation.Rows: + if orientation == Orientation.ROWS: $VBoxContainer/SpritesheetOptions/Orientation/LinesCountLabel.text = "Columns:" else: $VBoxContainer/SpritesheetOptions/Orientation/LinesCountLabel.text = "Rows:" @@ -516,13 +521,13 @@ func _on_BackgroundColor_color_changed(color : Color) -> void: func _on_Direction_item_selected(id : int) -> void: direction = id match id: - AnimationDirection.Forward: + AnimationDirection.FORWARD: animated_preview_current_frame = 0 - AnimationDirection.Backwards: + AnimationDirection.BACKWARDS: animated_preview_current_frame = processed_images.size() - 1 - AnimationDirection.PingPong: + AnimationDirection.PING_PONG: animated_preview_current_frame = 0 - pingpong_direction = AnimationDirection.Forward + pingpong_direction = AnimationDirection.FORWARD func _on_Resize_value_changed(value : float) -> void: @@ -579,39 +584,39 @@ func _on_FileExistsAlert_custom_action(action : String) -> void: $Popups/FileExistsAlert.hide() -var pingpong_direction = AnimationDirection.Forward +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: + 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: + AnimationDirection.BACKWARDS: if animated_preview_current_frame == 0: animated_preview_current_frame = processed_images.size() - 1 else: animated_preview_current_frame -= 1 - AnimationDirection.PingPong: + AnimationDirection.PING_PONG: match pingpong_direction: - AnimationDirection.Forward: + AnimationDirection.FORWARD: if animated_preview_current_frame == animated_preview_frames.size() - 1: - pingpong_direction = AnimationDirection.Backwards + 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: + 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 + pingpong_direction = AnimationDirection.FORWARD else: animated_preview_current_frame -= 1 diff --git a/Scripts/Dialogs/HSVDialog.gd b/Scripts/Dialogs/HSVDialog.gd index 62ed60c91..e98667076 100644 --- a/Scripts/Dialogs/HSVDialog.gd +++ b/Scripts/Dialogs/HSVDialog.gd @@ -1,5 +1,9 @@ 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 @@ -10,26 +14,26 @@ onready var val_spinbox = $MarginContainer/VBoxContainer/HBoxContainer/TextBoxes onready var preview = $MarginContainer/VBoxContainer/TextureRect -var current_layer:Image -var preview_image:Image -var preview_texture:ImageTexture -func _ready(): +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(): + +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(): + +func _on_Cancel_pressed() -> void: visible = false reset() -func _on_Apply_pressed(): + +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) @@ -39,6 +43,7 @@ func _on_Apply_pressed(): reset() visible = false + func reset() -> void: disconnect_signals() hue_slider.value = 0 @@ -49,6 +54,7 @@ func reset() -> void: 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) @@ -57,6 +63,7 @@ func update_preview() -> void: 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") @@ -65,6 +72,7 @@ func disconnect_signals() -> void: 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") @@ -73,24 +81,20 @@ func reconnect_signals() -> void: 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): + +func _on_Hue_value_changed(value : float) -> void: hue_spinbox.value = value hue_slider.value = value update_preview() -func _on_Saturation_value_changed(value): + +func _on_Saturation_value_changed(value : float) -> void: sat_spinbox.value = value sat_slider.value = value update_preview() -func _on_Value_value_changed(value): + +func _on_Value_value_changed(value : float) -> void: val_spinbox.value = value val_slider.value = value update_preview() - - - - - - - diff --git a/Scripts/Dialogs/ImportSprites.gd b/Scripts/Dialogs/ImportSprites.gd index f71214308..6eb942922 100644 --- a/Scripts/Dialogs/ImportSprites.gd +++ b/Scripts/Dialogs/ImportSprites.gd @@ -15,20 +15,25 @@ func _ready() -> void: 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 diff --git a/Scripts/Dialogs/OutlineDialog.gd b/Scripts/Dialogs/OutlineDialog.gd index 1711b2789..edc16bdb6 100644 --- a/Scripts/Dialogs/OutlineDialog.gd +++ b/Scripts/Dialogs/OutlineDialog.gd @@ -3,6 +3,7 @@ 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 diff --git a/Scripts/Dialogs/PreferencesDialog.gd b/Scripts/Dialogs/PreferencesDialog.gd index e4a139c41..f815807b9 100644 --- a/Scripts/Dialogs/PreferencesDialog.gd +++ b/Scripts/Dialogs/PreferencesDialog.gd @@ -37,6 +37,7 @@ 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) @@ -536,7 +537,7 @@ func _on_ShortcutSelector_confirmed() -> void: $Popups/ShortcutSelector.hide() -func _on_OpenLastProject_pressed(): +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") diff --git a/Scripts/Dialogs/ScaleImage.gd b/Scripts/Dialogs/ScaleImage.gd index 2caf86122..b49f4ec91 100644 --- a/Scripts/Dialogs/ScaleImage.gd +++ b/Scripts/Dialogs/ScaleImage.gd @@ -1,5 +1,6 @@ extends ConfirmationDialog + func _on_ScaleImage_confirmed() -> void: var width : int = $VBoxContainer/OptionsContainer/WidthValue.value var height : int = $VBoxContainer/OptionsContainer/HeightValue.value diff --git a/Scripts/Dialogs/SplashDialog.gd b/Scripts/Dialogs/SplashDialog.gd index ea4b2e514..92c2abfa6 100644 --- a/Scripts/Dialogs/SplashDialog.gd +++ b/Scripts/Dialogs/SplashDialog.gd @@ -7,6 +7,7 @@ onready var developed_by_label : Label = $"Contents/MarginContainer/Info/VBoxCon 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") @@ -26,40 +27,49 @@ func _on_SplashDialog_about_to_show() -> void: 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(): + +func _on_GithubButton_pressed() -> void: OS.shell_open("https://github.com/Orama-Interactive/Pixelorama") -func _on_DiscordButton_pressed(): + +func _on_DiscordButton_pressed() -> void: OS.shell_open("https://discord.gg/GTMtr8s") -func _on_NewBtn_pressed(): +func _on_NewBtn_pressed() -> void: Global.control.file_menu_id_pressed(0) visible = false -func _on_OpenBtn__pressed(): + +func _on_OpenBtn__pressed() -> void: Global.control.file_menu_id_pressed(1) visible = false -func _on_OpenLastBtn_pressed(): + +func _on_OpenLastBtn_pressed() -> void: Global.control.file_menu_id_pressed(2) visible = false -func _on_ImportBtn_pressed(): + +func _on_ImportBtn_pressed() -> void: Global.control.file_menu_id_pressed(5) visible = false diff --git a/Scripts/Drawers.gd b/Scripts/Drawers.gd index 49f275de0..99d7444b3 100644 --- a/Scripts/Drawers.gd +++ b/Scripts/Drawers.gd @@ -10,6 +10,7 @@ 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) @@ -19,9 +20,11 @@ class PixelPerfectDrawer extends Drawer: 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) diff --git a/Scripts/Global.gd b/Scripts/Global.gd index d3877439f..fd635a51b 100644 --- a/Scripts/Global.gd +++ b/Scripts/Global.gd @@ -117,8 +117,8 @@ var show_animation_timeline := true # Onion skinning options var onion_skinning := false -var onion_skinning_past_rate := 1 -var onion_skinning_future_rate := 1 +var onion_skinning_past_rate := 1.0 +var onion_skinning_future_rate := 1.0 var onion_skinning_blue_red := false # Brushes @@ -238,7 +238,6 @@ var onion_skinning_button : BaseButton var loop_animation_button : BaseButton var play_forward : BaseButton var play_backwards : BaseButton -var timeline_seconds : Control var layers_container : VBoxContainer var frames_container : VBoxContainer var tag_container : Control @@ -874,6 +873,7 @@ func update_left_custom_brush() -> void: left_brush_type_button.get_child(0).texture = custom_left_brush_texture + func update_right_custom_brush() -> void: if current_right_brush_type == Brush_Types.PIXEL: var pixel := Image.new() @@ -899,6 +899,7 @@ func update_right_custom_brush() -> void: right_brush_type_button.get_child(0).texture = custom_right_brush_texture + func blend_image_with_color(image : Image, color : Color, interpolate_factor : float) -> Image: var blended_image := Image.new() blended_image.copy_from(image) @@ -916,6 +917,7 @@ func blend_image_with_color(image : Image, color : Color, interpolate_factor : f blended_image.set_pixel(xx, yy, Color(0, 0, 0, 0)) return blended_image + # Algorithm based on http://members.chello.at/easyfilter/bresenham.html # This is not used for drawing, rather for finding the points required # for the mouse cursor/position indicator @@ -940,6 +942,7 @@ func plot_circle(r : int) -> Array: err += x * 2 + 1 return circle_points + func scale3X(sprite : Image, tol : float = 50) -> Image: var scaled = Image.new() scaled.create(sprite.get_width()*3, sprite.get_height()*3, false, Image.FORMAT_RGBA8) @@ -997,6 +1000,7 @@ func scale3X(sprite : Image, tol : float = 50) -> Image: sprite.unlock() return scaled + func rotxel(sprite : Image, angle : float) -> void: # If angle is simple, then nn rotation is the best @@ -1108,11 +1112,13 @@ func rotxel(sprite : Image, angle : float) -> void: sprite.unlock() aux.unlock() + func fake_rotsprite(sprite : Image, angle : float) -> void: sprite.copy_from(scale3X(sprite)) nn_rotate(sprite,angle) sprite.resize(sprite.get_width()/3,sprite.get_height()/3,0) + func nn_rotate(sprite : Image, angle : float) -> void: var aux : Image = Image.new() aux.copy_from(sprite) @@ -1132,14 +1138,17 @@ func nn_rotate(sprite : Image, angle : float) -> void: sprite.unlock() aux.unlock() + func similarColors(c1 : Color, c2 : Color, tol : float = 100) -> bool: var dist = colorDistance(c1, c2) return dist <= tol + func colorDistance(c1 : Color, c2 : Color) -> float: return sqrt(pow((c1.r - c2.r)*255, 2) + pow((c1.g - c2.g)*255, 2) + pow((c1.b - c2.b)*255, 2) + pow((c1.a - c2.a)*255, 2)) + func _exit_tree() -> void: config_cache.set_value("window", "screen", OS.current_screen) config_cache.set_value("window", "maximized", OS.window_maximized || OS.window_fullscreen) diff --git a/Scripts/Import.gd b/Scripts/Import.gd index 974e00ef8..2600597df 100644 --- a/Scripts/Import.gd +++ b/Scripts/Import.gd @@ -1,5 +1,6 @@ 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 [ @@ -304,6 +305,7 @@ func import_gpl(path : String) -> Palette: return result + func import_png_palette(path: String) -> Palette: var result: Palette = null @@ -332,4 +334,3 @@ func import_png_palette(path: String) -> Palette: result.name = path.substr(name_start, name_end - name_start) return result - pass diff --git a/Scripts/LayerContainer.gd b/Scripts/LayerContainer.gd index dc8d93b8d..66d3f2bbc 100644 --- a/Scripts/LayerContainer.gd +++ b/Scripts/LayerContainer.gd @@ -8,6 +8,7 @@ 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") @@ -36,10 +37,12 @@ func _ready() -> void: 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 @@ -47,9 +50,11 @@ func _on_LayerContainer_pressed() -> void: 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 @@ -58,13 +63,16 @@ func save_layer_name(new_name : String) -> void: 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]: diff --git a/Scripts/Main.gd b/Scripts/Main.gd index a11743ede..e8f054454 100644 --- a/Scripts/Main.gd +++ b/Scripts/Main.gd @@ -10,6 +10,7 @@ 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) @@ -245,10 +246,12 @@ func _input(event : InputEvent) -> void: 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 @@ -299,6 +302,7 @@ func file_menu_id_pressed(id : int) -> void: 8: # Quit show_quit_dialog() + func edit_menu_id_pressed(id : int) -> void: match id: 0: # Undo @@ -319,6 +323,7 @@ func edit_menu_id_pressed(id : int) -> void: $PreferencesDialog.popup_centered(Vector2(400, 280)) Global.can_draw = false + func view_menu_id_pressed(id : int) -> void: match id: 0: # Tile mode @@ -346,6 +351,7 @@ func view_menu_id_pressed(id : int) -> void: 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 @@ -434,6 +440,7 @@ func image_menu_id_pressed(id : int) -> void: $HSVDialog.popup_centered() Global.can_draw = false + func help_menu_id_pressed(id : int) -> void: match id: 0: # Splash Screen @@ -447,6 +454,7 @@ func help_menu_id_pressed(id : int) -> void: $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"): @@ -500,17 +508,23 @@ 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): @@ -603,10 +617,12 @@ 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 @@ -614,6 +630,7 @@ func _on_LeftBrushSizeEdit_value_changed(value) -> void: 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 @@ -621,9 +638,11 @@ func _on_RightBrushSizeEdit_value_changed(value) -> void: 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 diff --git a/Scripts/NotificationLabel.gd b/Scripts/NotificationLabel.gd index cfabbfb05..746ea8c8e 100644 --- a/Scripts/NotificationLabel.gd +++ b/Scripts/NotificationLabel.gd @@ -1,11 +1,12 @@ extends Label -# Called when the node enters the scene tree for the first time. + 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() diff --git a/Scripts/Palette/EditPalettePopup.gd b/Scripts/Palette/EditPalettePopup.gd index 35578398d..1ac0542f8 100644 --- a/Scripts/Palette/EditPalettePopup.gd +++ b/Scripts/Palette/EditPalettePopup.gd @@ -13,9 +13,11 @@ onready var palette_name_edit = $VBoxContainer/PaletteOptions/EditPaletteNameLin 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 @@ -28,6 +30,7 @@ func open(palette : String) -> void: 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 @@ -50,17 +53,20 @@ func _display_palette() -> void: 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) @@ -68,6 +74,7 @@ func on_move_swatch(from : int, to : int) -> void: re_index_swatches() + func _on_AddSwatchButton_pressed() -> void: var color : Color = color_picker.color var new_index : int = working_palette.colors.size() @@ -87,6 +94,7 @@ func _on_AddSwatchButton_pressed() -> void: 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) @@ -99,6 +107,7 @@ func _on_RemoveSwatchButton_pressed() -> void: 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 @@ -106,6 +115,7 @@ func re_index_swatches() -> void: 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() @@ -144,27 +154,33 @@ func _on_EditPaletteSaveButton_pressed() -> void: 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) diff --git a/Scripts/Palette/Palette.gd b/Scripts/Palette/Palette.gd index 44454fe3c..31d2074c9 100644 --- a/Scripts/Palette/Palette.gd +++ b/Scripts/Palette/Palette.gd @@ -1,35 +1,44 @@ class_name Palette extends Reference -func get_class(): - return "Palette" -func is_class(_name): - return _name == "Palette" or .is_class(_name) 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 @@ -38,10 +47,12 @@ func get_color(index : int) -> 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 := "" @@ -50,16 +61,19 @@ func get_color_data(index : int) -> String: 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 = "" @@ -68,16 +82,19 @@ func get_color_name(index : int) -> String: 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 @@ -87,6 +104,7 @@ func duplicate(): # -> Palette copy.colors.push_back(color.duplicate()) return copy + func _serialize() -> String: var result = "" var serialize_data : Dictionary = { @@ -102,6 +120,7 @@ func _serialize() -> String: return result + func deserialize(input_string : String): # -> Palette var result = get_script().new() @@ -126,6 +145,7 @@ func deserialize(input_string : String): # -> Palette return result + func load_from_file(path : String): # -> Palette var result = null # : Palette var file = File.new() diff --git a/Scripts/Palette/PaletteButton.gd b/Scripts/Palette/PaletteButton.gd index b5104e07e..df020d594 100644 --- a/Scripts/Palette/PaletteButton.gd +++ b/Scripts/Palette/PaletteButton.gd @@ -7,6 +7,7 @@ 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: @@ -17,8 +18,10 @@ func get_drag_data(_position): set_drag_preview(drag_icon) return data -func can_drop_data(_position, _data): + +func can_drop_data(_position, _data) -> bool: return true -func drop_data(_position, data): + +func drop_data(_position, data) -> void: emit_signal("on_drop_data", data.source_index, index) diff --git a/Scripts/Palette/PaletteColor.gd b/Scripts/Palette/PaletteColor.gd index e1378444e..97a501890 100644 --- a/Scripts/Palette/PaletteColor.gd +++ b/Scripts/Palette/PaletteColor.gd @@ -1,27 +1,35 @@ class_name PaletteColor extends Reference -func get_class(): - return "PaletteColor" -func is_class(_name): - return _name == "PaletteColor" or .is_class(_name) var color : Color = Color.black setget _set_color var data : String = "" setget _set_data var name : String = "no name" -func _init(new_color : Color = Color.black, new_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, @@ -29,6 +37,7 @@ func toDict() -> Dictionary: } return result + func fromDict(input_dict : Dictionary): # -> PaletteColor var result = get_script().new() @@ -37,6 +46,7 @@ func fromDict(input_dict : Dictionary): # -> PaletteColor return result + func duplicate(): # -> PaletteColor var copy = get_script().new() # : PaletteColor copy.data = data diff --git a/Scripts/Palette/PaletteContainer.gd b/Scripts/Palette/PaletteContainer.gd index 03166f118..cb0cfa1d7 100644 --- a/Scripts/Palette/PaletteContainer.gd +++ b/Scripts/Palette/PaletteContainer.gd @@ -15,12 +15,14 @@ func _ready() -> void: 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 @@ -28,6 +30,7 @@ func on_palette_select(palette_name : String) -> void: 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" @@ -35,10 +38,12 @@ func on_new_empty_palette() -> void: 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"): @@ -64,9 +69,11 @@ func on_palette_import_file_selected(path : String) -> void: 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) @@ -74,6 +81,7 @@ func on_new_palette_confirmed() -> void: 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 @@ -81,6 +89,7 @@ func add_palette_menu_id_pressed(id : int) -> void: 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() @@ -109,6 +118,7 @@ func create_new_palette(name : String, _from_palette : Palette) -> String: # Ret on_palette_select(name) return "" + func on_edit_palette() -> void: var palette : Palette = Global.palettes[current_palette] @@ -126,10 +136,12 @@ func on_edit_palette() -> void: 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 @@ -144,6 +156,7 @@ func _display_palette(palette : Palette) -> void: add_child(new_button) index += 1 + func on_color_select(index : int) -> void: var color : Color = Global.palettes[current_palette].get_color(index) @@ -154,6 +167,7 @@ func on_color_select(index : int) -> void: 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() @@ -179,6 +193,7 @@ func _load_palettes() -> void: 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: @@ -201,6 +216,7 @@ func get_palette_files(path : String ) -> Array: 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 @@ -230,6 +246,7 @@ func get_palette_priority_file_map(looking_paths: Array) -> Array: 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 @@ -243,6 +260,7 @@ func get_best_palette_file_location(looking_paths: Array, fname: String): # -> 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] diff --git a/Scripts/Rulers/Guides.gd b/Scripts/Rulers/Guides.gd index 2dcac21cc..61a1c3ca8 100644 --- a/Scripts/Rulers/Guides.gd +++ b/Scripts/Rulers/Guides.gd @@ -9,6 +9,7 @@ var mouse_pos := Vector2.ZERO var previous_points := points var type = Types.HORIZONTAL + func _ready() -> void: width = 0.1 default_color = Global.guide_color diff --git a/Scripts/Rulers/TimelineSeconds.gd b/Scripts/Rulers/TimelineSeconds.gd deleted file mode 100644 index d7c97fd88..000000000 --- a/Scripts/Rulers/TimelineSeconds.gd +++ /dev/null @@ -1,57 +0,0 @@ -extends Control - -const RULER_WIDTH := 16 - -var font := preload("res://Assets/Fonts/Roboto-Small.tres") -var major_subdivision := 2 -var minor_subdivision := 3 - -var first : Vector2 -var last : Vector2 - -# Code taken and modified from Godot's source code -func _draw() -> void: - var color := Color.white - if Global.theme_type == "Gold" || Global.theme_type == "Light": - color = Color.black - var transform := Transform2D() - var ruler_transform := Transform2D() - var major_subdivide := Transform2D() - var minor_subdivide := Transform2D() - var fps = Global.animation_timeline.fps - var horizontal_scroll = get_parent().get_node("FrameAndButtonContainer").get_node("ScrollContainer").scroll_horizontal - var starting_pos := Vector2(10, 10) - transform.x = Vector2(fps, fps) / 2.52 - - transform.origin = starting_pos - Vector2(horizontal_scroll, horizontal_scroll) - - var basic_rule := 100.0 - while(basic_rule * fps > 100): - basic_rule /= 2.0 - while(basic_rule * fps < 100): - basic_rule *= 2.0 - - 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(starting_pos) - last = (transform * ruler_transform * major_subdivide * minor_subdivide).affine_inverse().xform(rect_size - starting_pos) - - for i in range(ceil(first.x), last.x - 1): - 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) - var val = (ruler_transform * major_subdivide * minor_subdivide).xform(Vector2(i, 0)).x / 100 - val = stepify(val, 0.01) - draw_string(font, Vector2(position.x + RULER_WIDTH + 2, font.get_height() - 6), "%ss" % str(val), color) - 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) - else: - draw_line(Vector2(position.x + RULER_WIDTH, RULER_WIDTH * 0.66), Vector2(position.x + RULER_WIDTH, RULER_WIDTH), color) - -# warning-ignore:unused_argument -func _on_ScrollContainer_gui_input(event) -> void: - update() diff --git a/Scripts/SecondViewport.gd b/Scripts/SecondViewport.gd index d283f4366..bc6123ace 100644 --- a/Scripts/SecondViewport.gd +++ b/Scripts/SecondViewport.gd @@ -1,5 +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 diff --git a/Scripts/SelectionRectangle.gd b/Scripts/SelectionRectangle.gd index 5dbbb97ba..a646eca85 100644 --- a/Scripts/SelectionRectangle.gd +++ b/Scripts/SelectionRectangle.gd @@ -10,6 +10,7 @@ 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) @@ -17,8 +18,8 @@ func _ready() -> void: tex = ImageTexture.new() tex.create_from_image(img, 0) -# warning-ignore:unused_argument -func _process(delta : float) -> void: + +func _process(_delta : float) -> void: if Global.layers[Global.current_layer][2]: return var mouse_pos: Vector2 = get_local_mouse_position() - Global.canvas.location @@ -138,9 +139,11 @@ func _process(delta : float) -> void: 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 diff --git a/Scripts/TransparentChecker.gd b/Scripts/TransparentChecker.gd index 05c90f4b3..3a7b7c7d2 100644 --- a/Scripts/TransparentChecker.gd +++ b/Scripts/TransparentChecker.gd @@ -1,7 +1,7 @@ extends ColorRect -func _ready(): +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) diff --git a/Scripts/XDGDataPaths.gd b/Scripts/XDGDataPaths.gd index 2fa8da356..01f15c95d 100644 --- a/Scripts/XDGDataPaths.gd +++ b/Scripts/XDGDataPaths.gd @@ -109,6 +109,7 @@ func get_palette_write_path() -> String: 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)