diff --git a/Translations/Translations.pot b/Translations/Translations.pot index f95afab035f8..d2dc4a488652 100644 --- a/Translations/Translations.pot +++ b/Translations/Translations.pot @@ -2780,10 +2780,36 @@ msgstr "" msgid "Spring towards the end" msgstr "" -msgid "Silhouette" +#. Used to turn images into a singular color +msgid "Monochrome" msgstr "" -msgid "Blacks out the image and makes all opaque pixels a dark color." +#. A tooltip to tell users to hold the Shift key while clicking the remove button +msgid "Hold Shift while pressing to instantly remove" +msgstr "" + +#. Moves the reference image up in the list +msgid "Move the selected reference image to the right" +msgstr "" + +#. Moves the reference image down in the list +msgid "Move the selected reference image to the left" +msgstr "" + +#. Select a reference on the canvas +msgid "Selects a reference image on the canvas" +msgstr "" + +#. Moves the reference on the canvas +msgid "Move the selected reference image" +msgstr "" + +#. Rotates the reference on the canvas +msgid "Rotate the selected reference image" +msgstr "" + +#. Rotates the reference on the canvas +msgid "Scale the selected reference image" msgstr "" #. Used in checkbuttons (like on/off switches) that enable/disable something. diff --git a/assets/graphics/reference_images/move.png b/assets/graphics/reference_images/move.png new file mode 100644 index 000000000000..94cb1b21370b Binary files /dev/null and b/assets/graphics/reference_images/move.png differ diff --git a/assets/graphics/reference_images/move.png.import b/assets/graphics/reference_images/move.png.import new file mode 100644 index 000000000000..947b7c8b30fd --- /dev/null +++ b/assets/graphics/reference_images/move.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cedsyi8gf2n2i" +path="res://.godot/imported/move.png-ed702c0a346cd77f0de30a0cba128fc7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/graphics/reference_images/move.png" +dest_files=["res://.godot/imported/move.png-ed702c0a346cd77f0de30a0cba128fc7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/graphics/reference_images/rotate.png b/assets/graphics/reference_images/rotate.png new file mode 100644 index 000000000000..fa5bdb0165eb Binary files /dev/null and b/assets/graphics/reference_images/rotate.png differ diff --git a/assets/graphics/reference_images/rotate.png.import b/assets/graphics/reference_images/rotate.png.import new file mode 100644 index 000000000000..7db7ba35f51c --- /dev/null +++ b/assets/graphics/reference_images/rotate.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dtd43nvphu3jj" +path="res://.godot/imported/rotate.png-456db7ada5d7cd37fa30dc5557b226be.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/graphics/reference_images/rotate.png" +dest_files=["res://.godot/imported/rotate.png-456db7ada5d7cd37fa30dc5557b226be.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/graphics/reference_images/scale.png b/assets/graphics/reference_images/scale.png new file mode 100644 index 000000000000..b89d675123e6 Binary files /dev/null and b/assets/graphics/reference_images/scale.png differ diff --git a/assets/graphics/reference_images/scale.png.import b/assets/graphics/reference_images/scale.png.import new file mode 100644 index 000000000000..704b5d2f0ca0 --- /dev/null +++ b/assets/graphics/reference_images/scale.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://nfabwr5mgdir" +path="res://.godot/imported/scale.png-475926e4af79bb726ef59440df6e6f71.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/graphics/reference_images/scale.png" +dest_files=["res://.godot/imported/scale.png-475926e4af79bb726ef59440df6e6f71.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/graphics/reference_images/select.png b/assets/graphics/reference_images/select.png new file mode 100644 index 000000000000..62f811be7795 Binary files /dev/null and b/assets/graphics/reference_images/select.png differ diff --git a/assets/graphics/reference_images/select.png.import b/assets/graphics/reference_images/select.png.import new file mode 100644 index 000000000000..732423eba859 --- /dev/null +++ b/assets/graphics/reference_images/select.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d2m7enib3dplc" +path="res://.godot/imported/select.png-7c7e96d5ba897a73341e1d0445a3c991.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/graphics/reference_images/select.png" +dest_files=["res://.godot/imported/select.png-7c7e96d5ba897a73341e1d0445a3c991.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/project.godot b/project.godot index c6af7c8c1b1e..0183b31f44ed 100644 --- a/project.godot +++ b/project.godot @@ -820,6 +820,26 @@ onion_skinning_settings={ "deadzone": 0.5, "events": [] } +reference_rotate={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194326,"key_label":0,"unicode":0,"echo":false,"script":null) +] +} +reference_scale={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194328,"key_label":0,"unicode":0,"echo":false,"script":null) +] +} +reference_quick_menu={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":82,"key_label":0,"unicode":0,"echo":false,"script":null) +] +} +cancel_reference_transform={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194305,"key_label":0,"unicode":0,"echo":false,"script":null) +] +} [internationalization] diff --git a/src/Autoload/Global.gd b/src/Autoload/Global.gd index eaac6d2f0e14..ec5c014e765c 100644 --- a/src/Autoload/Global.gd +++ b/src/Autoload/Global.gd @@ -466,6 +466,10 @@ var cel_button_scene: PackedScene = load("res://src/UI/Timeline/CelButton.tscn") ## The perspective editor. It has the [param PerspectiveEditor.gd] script attached. @onready var perspective_editor := control.find_child("Perspective Editor") + +## The reference panel. It has the [param ReferencesPanel.gd] script attached. +@onready var reference_panel: ReferencesPanel = control.find_child("Reference Images") + ## The top menu container. It has the [param TopMenuContainer.gd] script attached. @onready var top_menu_container: Panel = control.find_child("TopMenuContainer") ## The label indicating cursor position. @@ -657,6 +661,10 @@ func _initialize_keychain() -> void: Keychain.InputAction.new("", "Transformation tools", false), "transform_copy_selection_content": Keychain.InputAction.new("", "Transformation tools", false), + "reference_rotate": Keychain.InputAction.new("", "Reference images", false), + "reference_scale": Keychain.InputAction.new("", "Reference images", false), + "reference_quick_menu": Keychain.InputAction.new("", "Reference images", false), + "cancel_reference_transform": Keychain.InputAction.new("", "Reference images", false) } Keychain.groups = { @@ -679,6 +687,7 @@ func _initialize_keychain() -> void: "Shape tools": Keychain.InputGroup.new("Tool modifiers"), "Selection tools": Keychain.InputGroup.new("Tool modifiers"), "Transformation tools": Keychain.InputGroup.new("Tool modifiers"), + "Reference images": Keychain.InputGroup.new("Canvas") } Keychain.ignore_actions = ["left_mouse", "right_mouse", "middle_mouse", "shift", "ctrl"] diff --git a/src/Autoload/OpenSave.gd b/src/Autoload/OpenSave.gd index af34fa898b17..c13dc5225938 100644 --- a/src/Autoload/OpenSave.gd +++ b/src/Autoload/OpenSave.gd @@ -750,7 +750,7 @@ func import_reference_image_from_path(path: String) -> void: var ri := ReferenceImage.new() ri.project = project ri.deserialize({"image_path": path}) - Global.canvas.add_child(ri) + Global.canvas.reference_image_container.add_child(ri) reference_image_imported.emit() @@ -760,7 +760,7 @@ func import_reference_image_from_image(image: Image) -> void: var ri := ReferenceImage.new() ri.project = project ri.create_from_image(image) - Global.canvas.add_child(ri) + Global.canvas.reference_image_container.add_child(ri) reference_image_imported.emit() diff --git a/src/Classes/Project.gd b/src/Classes/Project.gd index f04642aa361d..648939cb14f4 100644 --- a/src/Classes/Project.gd +++ b/src/Classes/Project.gd @@ -45,6 +45,7 @@ var animation_tags: Array[AnimationTag] = []: var guides: Array[Guide] = [] var brushes: Array[Image] = [] var reference_images: Array[ReferenceImage] = [] +var reference_index: int = -1 # The currently selected index ReferenceImage var vanishing_points := [] ## Array of Vanishing Points var fps := 6.0 @@ -250,6 +251,21 @@ func change_project() -> void: var edit_menu_popup: PopupMenu = Global.top_menu_container.edit_menu edit_menu_popup.set_item_disabled(Global.EditMenu.NEW_BRUSH, !has_selection) + # We loop through all the reference image nodes and the ones that are not apart + # of the current project we remove from the tree + # They will still be in memory though + for ri: ReferenceImage in Global.canvas.reference_image_container.get_children(): + if !reference_images.has(ri): + Global.canvas.reference_image_container.remove_child(ri) + # Now we loop through this projects reference images and add them back to the tree + var canvas_references := Global.canvas.reference_image_container.get_children() + for ri: ReferenceImage in reference_images: + if !canvas_references.has(ri) and !ri.is_inside_tree(): + Global.canvas.reference_image_container.add_child(ri) + + # Tell the reference images that the project changed + Global.reference_panel.project_changed() + var i := 0 for camera in Global.cameras: camera.rotation = cameras_rotation[i] @@ -870,3 +886,28 @@ func _update_layer_ui() -> void: for f in frames.size(): cel_hbox.get_child(f).layer = l cel_hbox.get_child(f).button_setup() + + +## Change the current reference image +func set_reference_image_index(new_index: int) -> void: + reference_index = clamp(-1, new_index, reference_images.size() - 1) + Global.canvas.reference_image_container.update_index(reference_index) + + +## Returns the reference image based on reference_index +func get_current_reference_image() -> ReferenceImage: + return get_reference_image(reference_index) + + +## Returns the reference image based on the index or null if index < 0 +func get_reference_image(index: int) -> ReferenceImage: + if index < 0 or index > reference_images.size() - 1: + return null + return reference_images[index] + + +## Reorders the position of the reference image in the tree / reference_images array +func reorder_reference_image(from: int, to: int) -> void: + var ri: ReferenceImage = reference_images.pop_at(from) + reference_images.insert(to, ri) + Global.canvas.reference_image_container.move_child(ri, to) diff --git a/src/Shaders/ReferenceImageShader.gdshader b/src/Shaders/ReferenceImageShader.gdshader new file mode 100644 index 000000000000..d1dc16bf3f45 --- /dev/null +++ b/src/Shaders/ReferenceImageShader.gdshader @@ -0,0 +1,21 @@ +shader_type canvas_item; + +// This shader gets applied to every single reference image. + + +uniform bool monochrome = false; +// Used because modulate does not work when monochrome is true +uniform vec4 monchrome_color : source_color; +// Clamp the color by using the greyscale of the image to identify the brightness of each pixel +uniform float clamping : hint_range(0.0, 1.0, 0.01) = 0.0; + +void fragment() { + // The original color + vec4 color = texture(TEXTURE, UV); + // Get the greyscale based on the brightest channel + float greyscale = max(max(color.r, color.g), color.b); + // Dont Use Alpha Channel past this statement! + if (greyscale < clamping) COLOR.a = 0.0; + // If we want the image to be onochrome we just set that pixels rgb color. + if (monochrome) COLOR.rgb = monchrome_color.rgb; +} diff --git a/src/Shaders/SilhouetteShader.gdshader b/src/Shaders/SilhouetteShader.gdshader deleted file mode 100644 index a661cefa75b0..000000000000 --- a/src/Shaders/SilhouetteShader.gdshader +++ /dev/null @@ -1,14 +0,0 @@ -shader_type canvas_item; - -uniform vec4 silhouette_color; -uniform bool show_silhouette; - -void fragment() { - vec4 color = texture(TEXTURE, UV); - if (show_silhouette && color.a > 0.0) { - color.rgb = silhouette_color.rgb; - COLOR.rgb = color.rgb; - } else { - COLOR = color; - } -} diff --git a/src/UI/Canvas/Canvas.gd b/src/UI/Canvas/Canvas.gd index 3fc32b8023b8..2ec91a9e12e2 100644 --- a/src/UI/Canvas/Canvas.gd +++ b/src/UI/Canvas/Canvas.gd @@ -26,6 +26,7 @@ var layer_metadata_texture := ImageTexture.new() @onready var mouse_guide_container := $MouseGuideContainer as Node2D @onready var gizmos_3d := $Gizmos3D as Node2D @onready var measurements := $Measurements as Node2D +@onready var reference_image_container := $ReferenceImages as Node2D func _ready() -> void: diff --git a/src/UI/Canvas/Canvas.tscn b/src/UI/Canvas/Canvas.tscn index 276703040635..48c7441ded6e 100644 --- a/src/UI/Canvas/Canvas.tscn +++ b/src/UI/Canvas/Canvas.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=21 format=3 uid="uid://ba24iuv55m4l3"] +[gd_scene load_steps=22 format=3 uid="uid://ba24iuv55m4l3"] [ext_resource type="Script" path="res://src/UI/Canvas/Canvas.gd" id="1"] [ext_resource type="Shader" path="res://src/Shaders/BlendLayers.gdshader" id="1_253dh"] @@ -16,6 +16,7 @@ [ext_resource type="Script" path="res://src/UI/Canvas/CropRect.gd" id="13"] [ext_resource type="Script" path="res://src/UI/Canvas/Gizmos3D.gd" id="14"] [ext_resource type="Script" path="res://src/UI/Canvas/Measurements.gd" id="16_nxilb"] +[ext_resource type="Script" path="res://src/UI/Canvas/ReferenceImages.gd" id="17_qfjb4"] [sub_resource type="ShaderMaterial" id="ShaderMaterial_6b0ox"] shader = ExtResource("1_253dh") @@ -93,3 +94,6 @@ script = ExtResource("14") [node name="Measurements" type="Node2D" parent="."] script = ExtResource("16_nxilb") + +[node name="ReferenceImages" type="Node2D" parent="."] +script = ExtResource("17_qfjb4") diff --git a/src/UI/Canvas/ReferenceImages.gd b/src/UI/Canvas/ReferenceImages.gd new file mode 100644 index 000000000000..727b2e1f8a3b --- /dev/null +++ b/src/UI/Canvas/ReferenceImages.gd @@ -0,0 +1,334 @@ +extends Node2D + +## This node contains [ReferenceImage] nodes + +signal reference_image_changed(index: int) + +enum Mode { SELECT, MOVE, ROTATE, SCALE } + +var mode: Mode = Mode.SELECT + +var index: int: + get: + return Global.current_project.reference_index +var drag_start_pos: Vector2 +var dragging := false +var lmb_held := false ## Holds whether the LBB is being held (use dragging for actual checks) + +# Original Transform +var og_pos: Vector2 +var og_scale: Vector2 +var og_rotation: float + +var undo_data: Dictionary + +var reference_menu := PopupMenu.new() + + +func _ready() -> void: + Global.camera.zoom_changed.connect(_update_on_zoom) + Global.control.get_node("Dialogs").add_child(reference_menu) + + # Makes sure that the dark overlay disappears when the popup is hidden + reference_menu.visibility_changed.connect(func(): Global.dialog_open(reference_menu.visible)) + # Emitted when a item is selected from the menu + reference_menu.id_pressed.connect(_reference_menu_id_pressed) + + +## Updates the index and configures the "gizmo" +func update_index(new_index: int) -> void: + index = new_index + reference_image_changed.emit(new_index) + queue_redraw() + + +func _input(event: InputEvent) -> void: + var local_mouse_pos := get_local_mouse_position() + + # Check if that event was for the quick menu (opened by the shortcut) + if event.is_action_pressed("reference_quick_menu"): + var list: Array[ReferenceImage] = Global.current_project.reference_images + populate_reference_menu(list, true) + reference_menu.position = Global.control.get_global_mouse_position() + reference_menu.popup() + + var ri: ReferenceImage = Global.current_project.get_current_reference_image() + + if !ri: + Global.can_draw = true + return + + # Check if want to cancelthe reference transform + if event.is_action_pressed("cancel_reference_transform") and dragging: + ri.position = og_pos + ri.scale = og_scale + ri.rotation = og_rotation + dragging = false + Global.can_draw = true + commit_undo("Cancel Transform Content", undo_data) + return + + if event is InputEventMouseButton: + if event.button_index == MOUSE_BUTTON_LEFT: + if event.is_pressed(): + dragging = false + lmb_held = true + undo_data = get_undo_data() + Global.can_draw = false + drag_start_pos = get_local_mouse_position() + # Set the original positions + og_pos = ri.position + og_scale = ri.scale + og_rotation = ri.rotation + + if !event.is_pressed(): + Global.can_draw = true + + if dragging: + commit_undo("Transform Content", undo_data) + else: + # Overlapping reference images + var overlapping: Array[ReferenceImage] = [] + + for idx: int in Global.current_project.reference_images.size(): + var r = Global.current_project.reference_images[idx] + # The bounding polygon + var p := get_reference_polygon(idx) + if Geometry2D.is_point_in_polygon(local_mouse_pos, p): + overlapping.append(r) + + # Some special cases + # 1. There is only one Reference Image + if overlapping.size() == 1: + var idx := overlapping[0].get_index() + Global.current_project.set_reference_image_index(idx) + # 2. There are more than 1 Reference Images + elif overlapping.size() > 1: + populate_reference_menu(overlapping, true) + reference_menu.position = Global.control.get_global_mouse_position() + reference_menu.popup() + # 3. There are no Reference Images + else: + Global.current_project.set_reference_image_index(-1) + + undo_data.clear() + dragging = false + lmb_held = false + + if event is InputEventMouseMotion: + # We check if the LMB is pressed and if we're not dragging then we force the + # draggin state. + # We dont use timers because it makes more sense to wait for the users mouse to move + # and that's what defines dragging. It would be smart to add a "deadzone" to determine + # if the mouse had moved enough. + if lmb_held and !dragging: + dragging = true + + if dragging: + var text := "" + + if mode == Mode.SELECT: + # Scale + if Input.is_action_pressed("reference_scale"): + scale_reference_image(local_mouse_pos, ri) + text = str( + "Moving: ", (og_scale * 100).floor(), " -> ", (ri.scale * 100).floor() + ) + # Rotate + elif Input.is_action_pressed("reference_rotate"): + rotate_reference_image(local_mouse_pos, ri) + text = str( + "Rotating: ", + floorf(rad_to_deg(og_rotation)), + "° -> ", + floorf(rad_to_deg(ri.rotation)), + "°" + ) + else: + move_reference_image(local_mouse_pos, ri) + text = str("Moving to: ", og_pos.floor(), " -> ", ri.position.floor()) + elif mode == Mode.MOVE: + move_reference_image(local_mouse_pos, ri) + text = str("Moving to: ", og_pos.floor(), " -> ", ri.position.floor()) + elif mode == Mode.ROTATE: + rotate_reference_image(local_mouse_pos, ri) + text = str( + "Rotating: ", + floorf(rad_to_deg(og_rotation)), + "° -> ", + floorf(rad_to_deg(ri.rotation)), + "°" + ) + elif mode == Mode.SCALE: + scale_reference_image(local_mouse_pos, ri) + text = str("Moving: ", (og_scale * 100).floor(), " -> ", (ri.scale * 100).floor()) + + Global.cursor_position_label.text = text + + queue_redraw() + + +## Uniformly scales the [ReferenceImage] using this nodes "local_mouse_position". +func scale_reference_image(mouse_pos: Vector2, img: ReferenceImage) -> void: + var s = ( + Vector2.ONE + * minf( + float(mouse_pos.x - drag_start_pos.x), + float(mouse_pos.y - drag_start_pos.y), + ) + ) + + img.scale = (og_scale + (s / 100.0)) + + +## Rotate the [ReferenceImage] using this nodes "local_mouse_position". +func rotate_reference_image(mouse_pos: Vector2, img: ReferenceImage) -> void: + var starting_angle = og_rotation - og_pos.angle_to_point(drag_start_pos) + var new_angle = img.position.angle_to_point(mouse_pos) + var angle = starting_angle + new_angle + angle = deg_to_rad(floorf(rad_to_deg(wrapf(angle, -PI, PI)))) + img.rotation = angle + + +## Move the [ReferenceImage] using this nodes "local_mouse_position". +func move_reference_image(mouse_pos: Vector2, img: ReferenceImage) -> void: + img.position = (mouse_pos - (drag_start_pos - og_pos)).floor() + + +## Makes a polygon that matches the transformed [ReferenceImage] +func get_reference_polygon(i: int) -> PackedVector2Array: + if i < 0: + return [] + + var ri: ReferenceImage = Global.current_project.reference_images[i] + var rect := ri.get_rect() + var poly = get_transformed_rect_polygon(rect, ri.transform) + return poly + + +## Returns a [PackedVector2Array] based on the corners of the [Rect2]. +## This function also transforms the polygon. +func get_transformed_rect_polygon(rect: Rect2, t: Transform2D) -> PackedVector2Array: + # First we scale the Rect2 + rect.position *= t.get_scale() + rect.size *= t.get_scale() + + # We create a polygon based on the Rect2 + var p: PackedVector2Array = [ + rect.position, + Vector2(rect.end.x, rect.position.y), + rect.end, + Vector2(rect.position.x, rect.end.y) + ] + + # Finally rotate and move the polygon + var final: PackedVector2Array = [] + for v: Vector2 in p: + var vert := v.rotated(t.get_rotation()) + t.get_origin() + final.append(vert) + + return final + + +func populate_reference_menu(items: Array[ReferenceImage], default := false): + reference_menu.clear() + # Default / Reset + if default: + reference_menu.add_item("None", 0) + reference_menu.add_separator() + + for ri: ReferenceImage in items: + var idx: int = ri.get_index() + 1 + var label: String = "(%o) %s" % [idx, ri.image_path] + # We trim the length of the title + label = label.left(22) + "..." + reference_menu.add_item(label, idx) + + +# When a id is pressed in the reference menu +func _reference_menu_id_pressed(id: int) -> void: + Global.can_draw = true + Global.current_project.set_reference_image_index(id - 1) + reference_menu.hide() + + +func remove_reference_image(idx: int) -> void: + var ri: ReferenceImage = Global.current_project.get_reference_image(idx) + Global.current_project.reference_images.remove_at(idx) + ri.queue_free() + Global.current_project.set_reference_image_index(-1) + Global.current_project.change_project() + Global.control.ui.find_child("Reference Images")._on_references_changed() + + +func _update_on_zoom() -> void: + queue_redraw() + + +func get_undo_data() -> Dictionary: + var ri: ReferenceImage = Global.current_project.get_current_reference_image() + + if !ri: + return {} + + var data := {} + data["position"] = ri.position + data["scale"] = ri.scale + data["rotation"] = ri.rotation + data["overlay_color"] = ri.overlay_color + data["filter"] = ri.filter + data["monochrome"] = ri.monochrome + data["color_clamping"] = ri.color_clamping + return data + + +func commit_undo(action: String, undo_data_tmp: Dictionary) -> void: + if !undo_data_tmp: + print("No undo data found for ReferenceImages.gd!") + return + + var ri: ReferenceImage = Global.current_project.get_current_reference_image() + if !ri: + print("No Reference Image ReferenceImages.gd!") + return + + var redo_data: Dictionary = get_undo_data() + var project := Global.current_project + + project.undos += 1 + project.undo_redo.create_action(action) + + for key in undo_data_tmp.keys(): + if redo_data.has(key): + project.undo_redo.add_do_property(ri, key, redo_data.get(key)) + project.undo_redo.add_undo_property(ri, key, undo_data_tmp.get(key)) + + project.undo_redo.add_do_method(Global.general_redo.bind(project)) + project.undo_redo.add_do_method(ri.change_properties) + project.undo_redo.add_undo_method(Global.general_undo.bind(project)) + project.undo_redo.add_undo_method(ri.change_properties) + + project.undo_redo.commit_action() + undo_data.clear() + + +func _draw() -> void: + if index < 0: + return + var line_width := 2.0 / Global.camera.zoom.x + # If we are dragging show where the Reference was coming from + if dragging: + var i: ReferenceImage = Global.current_project.get_current_reference_image() + var prev_transform = Transform2D(og_rotation, og_scale, 0.0, og_pos) + var prev_poly := get_transformed_rect_polygon(i.get_rect(), prev_transform) + prev_poly.append(prev_poly[0]) + draw_polyline(prev_poly, Color(1, 0.29, 0.29), line_width) + + # First we highlight the Reference Images under the mouse with yellow + for ri: ReferenceImage in Global.current_project.reference_images: + var p := get_transformed_rect_polygon(ri.get_rect(), ri.transform) + p.append(p[0]) + if ri.get_index() == index: + draw_polyline(p, Color(0.50, 0.99, 0.29), line_width) + elif Geometry2D.is_point_in_polygon(get_local_mouse_position(), p) and !dragging: + draw_polyline(p, Color(0.98, 0.80, 0.29), line_width) diff --git a/src/UI/ReferenceImages/ReferenceEdit.gd b/src/UI/ReferenceImages/ReferenceEdit.gd new file mode 100644 index 000000000000..d48bcba2edbe --- /dev/null +++ b/src/UI/ReferenceImages/ReferenceEdit.gd @@ -0,0 +1,272 @@ +extends VBoxContainer + +var undo_data: Dictionary + +var _prev_index: int = -1 +var _ignore_spinbox_changes: bool = false + +@onready var confirm_remove_dialog := $ConfirmRemoveDialog as ConfirmationDialog +@onready var timer := $Timer as Timer +@onready var references_container := Global.canvas.reference_image_container as Node2D + + +func _ready() -> void: + references_container.reference_image_changed.connect(_on_reference_image_changed) + + +func _update_properties(): + var ri: ReferenceImage = Global.current_project.get_current_reference_image() + if !ri: + return + # This is because otherwise a little dance will occur. + # This also breaks non-uniform scales (not supported UI-wise, but...) + _ignore_spinbox_changes = true + + # Image Path + if OS.get_name() == "Web": + $ImageOptions/ImagePath.disabled = true + else: + $ImageOptions/ImagePath.disabled = false + + if ri.image_path.is_empty(): + $ImageOptions/ImagePath.text = "(No Path)" + $ImageOptions/ImagePath.tooltip_text = "(No Path)" + else: + $ImageOptions/ImagePath.text = ri.image_path + $ImageOptions/ImagePath.tooltip_text = ri.image_path + + if !ri.texture: + $ImageOptions/WarningLabel.visible = true + $ImageOptions/ImagePath.visible = false + else: + $ImageOptions/WarningLabel.visible = false + $ImageOptions/ImagePath.visible = true + # Transform + $Options/Position/X.value = ri.position.x + $Options/Position/Y.value = ri.position.y + $Options/Position/X.max_value = ri.project.size.x + $Options/Position/Y.max_value = ri.project.size.y + $Options/Scale.value = ri.scale.x * 100 + $Options/Rotation.value = ri.rotation_degrees + # Color + $Options/Filter.button_pressed = ri.filter + $Options/Monochrome.button_pressed = ri.monochrome + $Options/Overlay.color = Color(ri.overlay_color, 1.0) + $Options/Opacity.value = ri.overlay_color.a * 100 + $Options/ColorClamping.value = ri.color_clamping * 100 + _ignore_spinbox_changes = false + + # Fore update the "gizmo" drawing + references_container.queue_redraw() + + +func _reset_properties() -> void: + # This is because otherwise a little dance will occur. + # This also breaks non-uniform scales (not supported UI-wise, but...) + _ignore_spinbox_changes = true + $ImageOptions/ImagePath.text = "None" + $ImageOptions/ImagePath.tooltip_text = "None" + $ImageOptions/ImagePath.disabled = true + $ImageOptions/WarningLabel.visible = false + $ImageOptions/ImagePath.visible = true + # Transform + $Options/Position/X.value = 0.0 + $Options/Position/Y.value = 0.0 + $Options/Position/X.max_value = 0.0 + $Options/Position/Y.max_value = 0.0 + $Options/Scale.value = 0.0 + $Options/Rotation.value = 0.0 + # Color + $Options/Filter.button_pressed = false + $Options/Monochrome.button_pressed = false + $Options/Overlay.color = Color.WHITE + $Options/Opacity.value = 0.0 + $Options/ColorClamping.value = 0.0 + _ignore_spinbox_changes = false + # Fore update the "gizmo" drawing + references_container.queue_redraw() + + +func _on_image_path_pressed() -> void: + var ri: ReferenceImage = Global.current_project.get_current_reference_image() + if !ri: + return + if ri.image_path.is_empty(): + print("No path for this image") + return + OS.shell_open(ri.image_path.get_base_dir()) + + +func _on_Monochrome_toggled(pressed: bool) -> void: + if _ignore_spinbox_changes: + return + var ri: ReferenceImage = Global.current_project.get_current_reference_image() + if !ri: + return + if timer.is_stopped(): + undo_data = references_container.get_undo_data() + timer.start() + ri.monochrome = pressed + + +func _on_Filter_toggled(pressed: bool) -> void: + if _ignore_spinbox_changes: + return + var ri: ReferenceImage = Global.current_project.get_current_reference_image() + if !ri: + return + if timer.is_stopped(): + undo_data = references_container.get_undo_data() + timer.start() + ri.filter = pressed + + +func _on_Reset_pressed(): + var ri: ReferenceImage = Global.current_project.get_current_reference_image() + if !ri: + return + var undo_data_tmp = references_container.get_undo_data() + ri.position_reset() + references_container.commit_undo("Reset Reference Image Position", undo_data_tmp) + + +func _on_Remove_pressed(): + var ri: ReferenceImage = Global.current_project.get_current_reference_image() + if !ri: + return + var index: int = Global.current_project.reference_index + if index > -1: + # If shift is pressed we just remove it without a dialog + if Input.is_action_pressed("shift"): + references_container.remove_reference_image(index) + else: + confirm_remove_dialog.position = Global.control.get_global_mouse_position() + confirm_remove_dialog.popup() + Global.dialog_open(true) + + +func _on_X_value_changed(value: float): + if _ignore_spinbox_changes: + return + var ri: ReferenceImage = Global.current_project.get_current_reference_image() + if !ri: + return + if timer.is_stopped(): + undo_data = references_container.get_undo_data() + timer.start() + ri.position.x = value + + +func _on_Y_value_changed(value: float): + if _ignore_spinbox_changes: + return + var ri: ReferenceImage = Global.current_project.get_current_reference_image() + if !ri: + return + if timer.is_stopped(): + undo_data = references_container.get_undo_data() + timer.start() + ri.position.y = value + + +func _on_Scale_value_changed(value: float): + if _ignore_spinbox_changes: + return + var ri: ReferenceImage = Global.current_project.get_current_reference_image() + if !ri: + return + if timer.is_stopped(): + undo_data = references_container.get_undo_data() + timer.start() + ri.scale.x = value / 100 + ri.scale.y = value / 100 + + +func _on_Rotation_value_changed(value: float): + if _ignore_spinbox_changes: + return + var ri: ReferenceImage = Global.current_project.get_current_reference_image() + if !ri: + return + if timer.is_stopped(): + undo_data = references_container.get_undo_data() + timer.start() + ri.rotation_degrees = value + + +func _on_Overlay_color_changed(color: Color): + if _ignore_spinbox_changes: + return + var ri: ReferenceImage = Global.current_project.get_current_reference_image() + if !ri: + return + if timer.is_stopped(): + undo_data = references_container.get_undo_data() + timer.start() + ri.overlay_color = Color(color, ri.overlay_color.a) + + +func _on_Opacity_value_changed(value: float): + if _ignore_spinbox_changes: + return + var ri: ReferenceImage = Global.current_project.get_current_reference_image() + if !ri: + return + if timer.is_stopped(): + undo_data = references_container.get_undo_data() + timer.start() + ri.overlay_color.a = value / 100 + + +func _on_ColorClamping_value_changed(value: float): + if _ignore_spinbox_changes: + return + var ri: ReferenceImage = Global.current_project.get_current_reference_image() + if !ri: + return + if timer.is_stopped(): + undo_data = references_container.get_undo_data() + timer.start() + ri.color_clamping = value / 100 + + +func _on_timer_timeout() -> void: + references_container.commit_undo("Reference Image Changed", undo_data) + + +func _on_confirm_remove_dialog_confirmed() -> void: + var index: int = Global.current_project.reference_index + if index > -1: + references_container.remove_reference_image(index) + Global.dialog_open(false) + + +func _on_confirm_remove_dialog_canceled() -> void: + Global.dialog_open(false) + + +func _on_reference_image_porperties_changed() -> void: + _update_properties() + + +func _on_reference_image_changed(index: int) -> void: + # This is a check to make sure that the index is not more than the amount of references + if _prev_index > Global.current_project.reference_images.size() - 1: + return + # Disconnect the previously selected one + if _prev_index > -1: + var prev_ri: ReferenceImage = Global.current_project.get_reference_image(_prev_index) + if prev_ri.properties_changed.is_connected(_on_reference_image_porperties_changed): + prev_ri.properties_changed.disconnect(_on_reference_image_porperties_changed) + # Connect the new Reference image (if it is one) + if index > -1: + Global.current_project.reference_images[index].properties_changed.connect( + _on_reference_image_porperties_changed + ) + + _prev_index = index + + if index < 0: + _reset_properties() + else: + _update_properties() diff --git a/src/UI/ReferenceImages/ReferenceImage.gd b/src/UI/ReferenceImages/ReferenceImage.gd index 60eec8fcd0c5..3600acedf40a 100644 --- a/src/UI/ReferenceImages/ReferenceImage.gd +++ b/src/UI/ReferenceImages/ReferenceImage.gd @@ -6,15 +6,38 @@ signal properties_changed var project := Global.current_project -var shader := preload("res://src/Shaders/SilhouetteShader.gdshader") +var shader := preload("res://src/Shaders/ReferenceImageShader.gdshader") var image_path := "" -var filter := false -var silhouette := false +var filter := false: + set(value): + filter = value + if value: + texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR + else: + texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST +var monochrome := false: + set(value): + monochrome = value + if material: + get_material().set_shader_parameter("monochrome", value) +var overlay_color := Color.WHITE: + set(value): + overlay_color = value + modulate = value + if material: + get_material().set_shader_parameter("monchrome_color", value) +var color_clamping := 0.0: + set(value): + color_clamping = value + if material: + get_material().set_shader_parameter("clamping", value) func _ready() -> void: project.reference_images.append(self) + # Make this show behind parent because we want to use _draw() to draw over it + show_behind_parent = true func change_properties() -> void: @@ -24,6 +47,7 @@ func change_properties() -> void: ## Resets the position and scale of the reference image. func position_reset() -> void: position = project.size / 2.0 + rotation_degrees = 0.0 if texture != null: scale = ( Vector2.ONE @@ -43,12 +67,14 @@ func serialize() -> Dictionary: "y": position.y, "scale_x": scale.x, "scale_y": scale.y, - "modulate_r": modulate.r, - "modulate_g": modulate.g, - "modulate_b": modulate.b, - "modulate_a": modulate.a, + "rotation_degrees": rotation_degrees, + "overlay_color_r": overlay_color.r, + "overlay_color_g": overlay_color.g, + "overlay_color_b": overlay_color.b, + "overlay_color_a": overlay_color.a, "filter": filter, - "silhouette": silhouette, + "monochrome": monochrome, + "color_clamping": color_clamping, "image_path": image_path } @@ -57,7 +83,7 @@ func serialize() -> Dictionary: ## Be aware that new ReferenceImages are created via deserialization. ## This is because deserialization sets up some nice defaults. func deserialize(d: Dictionary) -> void: - modulate = Color(1, 1, 1, 0.5) + overlay_color = Color(1, 1, 1, 0.5) if d.has("image_path"): # Note that reference images are referred to by path. # These images may be rather big. @@ -69,9 +95,6 @@ func deserialize(d: Dictionary) -> void: # Apply the silhouette shader var mat := ShaderMaterial.new() mat.shader = shader - # TODO: Lsbt - Add a option in prefrences to customize the color - # This color is almost black because it is less harsh - mat.set_shader_parameter("silhouette_color", Color(0.069, 0.069326, 0.074219)) set_material(mat) # Now that the image may have been established... @@ -82,20 +105,24 @@ func deserialize(d: Dictionary) -> void: position.y = d["y"] if d.has("scale_x"): scale.x = d["scale_x"] + if d.has("rotation_degrees"): + rotation_degrees = d["rotation_degrees"] if d.has("scale_y"): scale.y = d["scale_y"] - if d.has("modulate_r"): - modulate.r = d["modulate_r"] - if d.has("modulate_g"): - modulate.g = d["modulate_g"] - if d.has("modulate_b"): - modulate.b = d["modulate_b"] - if d.has("modulate_a"): - modulate.a = d["modulate_a"] + if d.has("overlay_color_r"): + overlay_color.r = d["overlay_color_r"] + if d.has("overlay_color_g"): + overlay_color.g = d["overlay_color_g"] + if d.has("overlay_color_b"): + overlay_color.b = d["overlay_color_b"] + if d.has("overlay_color_a"): + overlay_color.a = d["overlay_color_a"] if d.has("filter"): filter = d["filter"] - if d.has("silhouette"): - get_material().set_shader_parameter("show_silhouette", d["silhouette"]) + if d.has("monochrome"): + monochrome = d["monochrome"] + if d.has("color_clamping"): + color_clamping = d["color_clamping"] change_properties() diff --git a/src/UI/ReferenceImages/ReferenceImageButton.gd b/src/UI/ReferenceImages/ReferenceImageButton.gd index f162d99b3d1f..79f4984da442 100644 --- a/src/UI/ReferenceImages/ReferenceImageButton.gd +++ b/src/UI/ReferenceImages/ReferenceImageButton.gd @@ -1,98 +1,67 @@ -extends Container -## UI to handle reference image editing. +extends Button -var element: ReferenceImage -var _ignore_spinbox_changes := false +func _get_drag_data(_at_position: Vector2) -> Variant: + var index := get_index() - 1 + # If the index < 0 then that means this button is the "reset button" + if index < 0: + return null -func _ready(): - if OS.get_name() == "Web": - $Interior/PathHeader/Path.visible = false - $Interior/PathHeader/PathHTML.text = element.image_path - else: - $Interior/PathHeader/PathHTML.visible = false - $Interior/PathHeader/Path.text = element.image_path - - if !element.texture: - $Interior/PreviewAndOptions/PreviewPanel/Warning.text = "Image not found!" - else: - $Interior/PreviewAndOptions/PreviewPanel/Preview.texture = element.texture - element.properties_changed.connect(_update_properties) - _update_properties() - + set_drag_preview(self.duplicate()) -func _update_properties(): - # This is because otherwise a little dance will occur. - # This also breaks non-uniform scales (not supported UI-wise, but...) - _ignore_spinbox_changes = true - $Interior/PreviewAndOptions/Options/Scale.value = element.scale.x * 100 - $Interior/PreviewAndOptions/Options/Position/X.value = element.position.x - $Interior/PreviewAndOptions/Options/Position/Y.value = element.position.y - $Interior/PreviewAndOptions/Options/Position/X.max_value = element.project.size.x - $Interior/PreviewAndOptions/Options/Position/Y.max_value = element.project.size.y - $Interior/PreviewAndOptions/Options/Opacity.value = element.modulate.a * 100 - $Interior/OtherOptions/ApplyFilter.button_pressed = element.filter - _ignore_spinbox_changes = false + var data := ["ReferenceImage", index] + return data -func _on_Reset_pressed(): - element.position_reset() - element.change_properties() +func _can_drop_data(_at_position: Vector2, data: Variant) -> bool: + if typeof(data) != TYPE_ARRAY: + Global.reference_panel.drag_highlight.visible = false + return false + if data[0] != "ReferenceImage": + Global.reference_panel.drag_highlight.visible = false + return false -func _on_Remove_pressed(): - var index = Global.current_project.reference_images.find(element) - if index != -1: - queue_free() - element.queue_free() - Global.current_project.reference_images.remove_at(index) - OpenSave.reference_image_imported.emit() + var index := get_index() - 1 + var from_index: int = data[1] + # If the index < 0 then that means this button is the "reset button" + # Or we are trying to drop on the same button + if index < 0 or index == from_index: + Global.reference_panel.drag_highlight.visible = false + return false + var side: int = -1 + if get_local_mouse_position().x > size.x / 2: + side = 1 -func _on_Scale_value_changed(value: float): - if _ignore_spinbox_changes: - return - element.scale.x = value / 100 - element.scale.y = value / 100 - element.change_properties() + var region := Rect2(global_position + Vector2(3, 0), Vector2(6, size.y)) + # Get the side + if side == 1: + region.position.x = (size.x + global_position.x) - 3 -func _on_X_value_changed(value: float): - if _ignore_spinbox_changes: - return - element.position.x = value - element.change_properties() + Global.reference_panel.drag_highlight.visible = true + Global.reference_panel.drag_highlight.position = region.position + Global.reference_panel.drag_highlight.size = region.size + return true -func _on_Y_value_changed(value: float): - if _ignore_spinbox_changes: - return - element.position.y = value - element.change_properties() +func _drop_data(_at_position: Vector2, data: Variant) -> void: + var from_index: int = data[1] + var to_index := get_index() -func _on_Opacity_value_changed(value: float): - if _ignore_spinbox_changes: - return - element.modulate.a = value / 100 - element.change_properties() - - -func _on_Path_pressed() -> void: - OS.shell_open($Interior/PathHeader/Path.text.get_base_dir()) - + if get_local_mouse_position().x > size.x / 2: + if from_index > to_index: + to_index += 1 + print("Help mee") + else: + if from_index < to_index: + to_index -= 1 -func _on_Silhouette_toggled(button_pressed: bool) -> void: - element.silhouette = button_pressed - element.get_material().set_shader_parameter("show_silhouette", button_pressed) - element.change_properties() + Global.reference_panel.reorder_reference_image(from_index, to_index - 1, false) + #Global.current_project.reorder_reference_image(from_index, to_index - 1) + #Global.reference_panel._on_references_changed() -func _on_ApplyFilter_toggled(button_pressed: bool) -> void: - element.filter = button_pressed - if element.texture: - if element.filter: - element.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR - else: - element.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST - element.change_properties() + #Global.reference_panel.drag_highlight.visible = false diff --git a/src/UI/ReferenceImages/ReferenceImageButton.tscn b/src/UI/ReferenceImages/ReferenceImageButton.tscn index 9ffa9d2f8bb8..be95b65851e1 100644 --- a/src/UI/ReferenceImages/ReferenceImageButton.tscn +++ b/src/UI/ReferenceImages/ReferenceImageButton.tscn @@ -1,128 +1,21 @@ -[gd_scene load_steps=3 format=3 uid="uid://5quwfcfl5o1e"] - -[ext_resource type="PackedScene" uid="uid://yjhp0ssng2mp" path="res://src/UI/Nodes/ValueSlider.tscn" id="1"] -[ext_resource type="Script" path="res://src/UI/ReferenceImages/ReferenceImageButton.gd" id="2"] - -[node name="ReferenceImageButton" type="PanelContainer"] -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_right = -969.0 -offset_bottom = -581.0 -size_flags_horizontal = 3 -script = ExtResource("2") - -[node name="Interior" type="VBoxContainer" parent="."] -layout_mode = 2 - -[node name="PathHeader" type="HBoxContainer" parent="Interior"] -layout_mode = 2 -theme_override_constants/separation = 0 - -[node name="Path" type="LinkButton" parent="Interior/PathHeader"] -modulate = Color(0.552941, 1, 0.298039, 1) -layout_mode = 2 -size_flags_horizontal = 3 -underline = 1 - -[node name="PathHTML" type="Label" parent="Interior/PathHeader"] -self_modulate = Color(0.552941, 1, 0.298039, 1) -layout_mode = 2 -size_flags_horizontal = 3 - -[node name="HSeparator" type="HSeparator" parent="Interior/PathHeader"] -layout_mode = 2 -size_flags_horizontal = 3 - -[node name="PreviewAndOptions" type="HBoxContainer" parent="Interior"] -layout_mode = 2 - -[node name="Options" type="GridContainer" parent="Interior/PreviewAndOptions"] -layout_mode = 2 -size_flags_horizontal = 3 -columns = 2 - -[node name="PosLabel" type="Label" parent="Interior/PreviewAndOptions/Options"] -layout_mode = 2 -text = "Position:" - -[node name="Position" type="HBoxContainer" parent="Interior/PreviewAndOptions/Options"] -layout_mode = 2 -size_flags_horizontal = 3 - -[node name="X" parent="Interior/PreviewAndOptions/Options/Position" instance=ExtResource("1")] -layout_mode = 2 -allow_greater = true -allow_lesser = true - -[node name="Y" parent="Interior/PreviewAndOptions/Options/Position" instance=ExtResource("1")] -layout_mode = 2 -allow_greater = true -allow_lesser = true - -[node name="ScaleLabel" type="Label" parent="Interior/PreviewAndOptions/Options"] -layout_mode = 2 -text = "Scale:" - -[node name="Scale" parent="Interior/PreviewAndOptions/Options" instance=ExtResource("1")] -layout_mode = 2 -allow_greater = true -allow_lesser = true - -[node name="OpacityLabel" type="Label" parent="Interior/PreviewAndOptions/Options"] -layout_mode = 2 -text = "Opacity:" - -[node name="Opacity" parent="Interior/PreviewAndOptions/Options" instance=ExtResource("1")] -layout_mode = 2 - -[node name="PreviewPanel" type="Panel" parent="Interior/PreviewAndOptions"] -custom_minimum_size = Vector2(80, 80) -layout_mode = 2 - -[node name="Warning" type="Label" parent="Interior/PreviewAndOptions/PreviewPanel"] -layout_mode = 0 -anchor_right = 1.0 -anchor_bottom = 1.0 - -[node name="Preview" type="TextureRect" parent="Interior/PreviewAndOptions/PreviewPanel"] -layout_mode = 0 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_left = 2.0 -offset_top = 2.0 -offset_right = -2.0 -offset_bottom = -2.0 -expand_mode = 1 - -[node name="OtherOptions" type="HBoxContainer" parent="Interior"] -layout_mode = 2 -alignment = 2 - -[node name="ApplyFilter" type="CheckBox" parent="Interior/OtherOptions"] -layout_mode = 2 -tooltip_text = "Uses a magnifying filter, to enable smooth zooming in of the texture." -text = "Apply Filter" - -[node name="Silhouette" type="CheckBox" parent="Interior/OtherOptions"] -layout_mode = 2 -text = "Silhouette" - -[node name="Reset" type="Button" parent="Interior/OtherOptions"] -layout_mode = 2 -text = "Reset" - -[node name="Remove" type="Button" parent="Interior/OtherOptions"] -layout_mode = 2 -theme_override_colors/font_color = Color(1, 0.266667, 0.266667, 1) -text = "Remove" - -[connection signal="pressed" from="Interior/PathHeader/Path" to="." method="_on_Path_pressed"] -[connection signal="value_changed" from="Interior/PreviewAndOptions/Options/Position/X" to="." method="_on_X_value_changed"] -[connection signal="value_changed" from="Interior/PreviewAndOptions/Options/Position/Y" to="." method="_on_Y_value_changed"] -[connection signal="value_changed" from="Interior/PreviewAndOptions/Options/Scale" to="." method="_on_Scale_value_changed"] -[connection signal="value_changed" from="Interior/PreviewAndOptions/Options/Opacity" to="." method="_on_Opacity_value_changed"] -[connection signal="toggled" from="Interior/OtherOptions/ApplyFilter" to="." method="_on_ApplyFilter_toggled"] -[connection signal="toggled" from="Interior/OtherOptions/Silhouette" to="." method="_on_Silhouette_toggled"] -[connection signal="pressed" from="Interior/OtherOptions/Reset" to="." method="_on_Reset_pressed"] -[connection signal="pressed" from="Interior/OtherOptions/Remove" to="." method="_on_Remove_pressed"] +[gd_scene load_steps=4 format=3 uid="uid://by3300fom3plf"] + +[ext_resource type="Script" path="res://src/UI/ReferenceImages/ReferenceImageButton.gd" id="1_nf0dd"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_53xjd"] +draw_center = false +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_tuwm6"] + +[node name="ReferenceImageButton" type="Button"] +custom_minimum_size = Vector2(64, 64) +theme_override_styles/pressed = SubResource("StyleBoxFlat_53xjd") +theme_override_styles/focus = SubResource("StyleBoxEmpty_tuwm6") +toggle_mode = true +icon_alignment = 1 +expand_icon = true +script = ExtResource("1_nf0dd") diff --git a/src/UI/ReferenceImages/ReferencesPanel.gd b/src/UI/ReferenceImages/ReferencesPanel.gd index 775074c0f4f8..27a55b44c354 100644 --- a/src/UI/ReferenceImages/ReferencesPanel.gd +++ b/src/UI/ReferenceImages/ReferencesPanel.gd @@ -1,27 +1,157 @@ class_name ReferencesPanel -extends VBoxContainer +extends PanelContainer ## Panel for reference image management -var reference_image_button_tscn := preload("res://src/UI/ReferenceImages/ReferenceImageButton.tscn") -@onready var list = $"Scroll/List" +const ReferenceImageButton = preload("res://src/UI/ReferenceImages/ReferenceImageButton.tscn") + +var list_btn_group := ButtonGroup.new() +var transform_button_group: ButtonGroup +var _ignore_ui_updates := false + +@onready var list := %List as HBoxContainer +@onready var drag_highlight := $Overlay/DragHighlight as ColorRect +@onready var remove_btn := $ScrollContainer/Container/ReferenceEdit/ImageOptions/Remove as Button +@onready var transform_tools_btns := $ScrollContainer/Container/Tools/TransformTools func _ready() -> void: - Global.project_changed.connect(_update_reference_images) - OpenSave.reference_image_imported.connect(_update_reference_images) + transform_button_group = transform_tools_btns.get_child(0).button_group + transform_button_group.pressed.connect(_on_transform_tool_button_group_pressed) + list_btn_group.pressed.connect(_on_reference_image_button_pressed) + Global.canvas.reference_image_container.reference_image_changed.connect( + _on_reference_image_changed + ) + OpenSave.reference_image_imported.connect(_on_references_changed) + # We call this function to update the buttons + _on_references_changed() + _update_ui() + + +func _notification(what: int) -> void: + if what == NOTIFICATION_DRAG_END: + drag_highlight.hide() + + +func _on_transform_tool_button_group_pressed(button: Button) -> void: + Global.canvas.reference_image_container.mode = button.get_index() + + +func _on_move_image_left_pressed() -> void: + var index: int = Global.current_project.reference_index + reorder_reference_image(index, index - 1) + + +func _on_move_image_right_pressed() -> void: + var index: int = Global.current_project.reference_index + reorder_reference_image(index, index + 1) + + +## This method allows you to reoreder reference image with undo redo support +## Please use this and not the method with the same name in project.gd +func reorder_reference_image(from: int, to: int, update_reference_index := true) -> void: + var project := Global.current_project + project.undo_redo.create_action("Reorder Reference Image") + project.undo_redo.add_do_method(project.reorder_reference_image.bind(from, to)) + project.undo_redo.add_do_method(Global.reference_panel._on_references_changed) + if update_reference_index: + project.undo_redo.add_do_method(project.set_reference_image_index.bind(to)) + else: + project.undo_redo.add_undo_method( + project.set_reference_image_index.bind(project.reference_index) + ) + project.undo_redo.add_do_method(_update_ui) + + project.undo_redo.add_undo_method(project.reorder_reference_image.bind(to, from)) + project.undo_redo.add_undo_method(Global.reference_panel._on_references_changed) + if update_reference_index: + project.undo_redo.add_undo_method(project.set_reference_image_index.bind(from)) + else: + project.undo_redo.add_undo_method( + project.set_reference_image_index.bind(project.reference_index) + ) + + project.undo_redo.add_undo_method(_update_ui) + + project.undo_redo.commit_action() + +func _update_ui() -> void: + var index: int = Global.current_project.reference_index + + # Enable the buttons as a default + %MoveImageRightBtn.disabled = false + %MoveImageLeftBtn.disabled = false + + if index == -1: + %MoveImageLeftBtn.disabled = true + %MoveImageRightBtn.disabled = true + if index == 0: + %MoveImageLeftBtn.disabled = true + if index == Global.current_project.reference_images.size() - 1: + %MoveImageRightBtn.disabled = true + + if %MoveImageLeftBtn.disabled: + %MoveImageLeftBtn.mouse_default_cursor_shape = CURSOR_FORBIDDEN + else: + %MoveImageLeftBtn.mouse_default_cursor_shape = CURSOR_POINTING_HAND + + if %MoveImageRightBtn.disabled: + %MoveImageRightBtn.mouse_default_cursor_shape = CURSOR_FORBIDDEN + else: + %MoveImageRightBtn.mouse_default_cursor_shape = CURSOR_POINTING_HAND + + # Update the remove button + remove_btn.disabled = index == -1 + if remove_btn.disabled: + remove_btn.mouse_default_cursor_shape = CURSOR_FORBIDDEN + else: + remove_btn.mouse_default_cursor_shape = CURSOR_POINTING_HAND + + +func _on_reference_image_button_pressed(button: Button) -> void: + # We subtract 1 because we already have a default button to "select no reference image + Global.current_project.set_reference_image_index(button.get_index() - 1) + + +# In case the signal is emitted for another node and not from a pressed button +func _on_reference_image_changed(index: int) -> void: + _update_ui() + # Update the buttons to show which one is pressed + if list_btn_group.get_buttons().size() > 0: + # First we loop through the buttons to "unpress them all" + for b: Button in list_btn_group.get_buttons(): + b.set_pressed_no_signal(false) + # Then we get the wanted button and we press it + list_btn_group.get_buttons()[index + 1].set_pressed_no_signal(true) + + +func project_changed() -> void: + var project_reference_index := Global.current_project.reference_index + _on_references_changed() + _update_ui() + Global.current_project.set_reference_image_index(project_reference_index) + + +func _on_references_changed(): + # When we change the project we set the default + Global.current_project.set_reference_image_index(-1) -func _update_reference_images(): for c in list.get_children(): + if c is Button: + c.button_group = null c.queue_free() - # Just do this here because I'm not sure where it's done. - # By all means, change this! - for ref in Global.canvas.get_children(): - if ref is ReferenceImage: - ref.visible = false + + # The default button + var default = ReferenceImageButton.instantiate() + default.button_group = list_btn_group + default.text = "none" + default.button_pressed = true + list.add_child(default) + # And update. for ref in Global.current_project.reference_images: - ref.visible = true - var l = reference_image_button_tscn.instantiate() - l.element = ref + var l: Button = ReferenceImageButton.instantiate() + l.button_group = list_btn_group + if ref.texture: + l.icon = ref.texture list.add_child(l) diff --git a/src/UI/ReferenceImages/ReferencesPanel.tscn b/src/UI/ReferenceImages/ReferencesPanel.tscn index 05ccca9bc281..b735ee78c2f0 100644 --- a/src/UI/ReferenceImages/ReferencesPanel.tscn +++ b/src/UI/ReferenceImages/ReferencesPanel.tscn @@ -1,40 +1,399 @@ -[gd_scene load_steps=2 format=3 uid="uid://b75xk2ix8xxml"] +[gd_scene load_steps=13 format=3 uid="uid://cxhs8qy5ilufv"] [ext_resource type="Script" path="res://src/UI/ReferenceImages/ReferencesPanel.gd" id="1"] +[ext_resource type="Script" path="res://src/UI/Nodes/ValueSlider.gd" id="2_1qu4x"] +[ext_resource type="Texture2D" uid="uid://d1oxrkwndy5fi" path="res://assets/graphics/timeline/move_arrow.png" id="2_uqbp6"] +[ext_resource type="PackedScene" uid="uid://yjhp0ssng2mp" path="res://src/UI/Nodes/ValueSlider.tscn" id="3_1w6gu"] +[ext_resource type="Script" path="res://src/UI/ReferenceImages/ReferenceEdit.gd" id="3_skjtb"] +[ext_resource type="Texture2D" uid="uid://d2m7enib3dplc" path="res://assets/graphics/reference_images/select.png" id="3_us8st"] +[ext_resource type="Texture2D" uid="uid://cedsyi8gf2n2i" path="res://assets/graphics/reference_images/move.png" id="4_8mlcg"] +[ext_resource type="Texture2D" uid="uid://dtd43nvphu3jj" path="res://assets/graphics/reference_images/rotate.png" id="5_ifey7"] +[ext_resource type="Texture2D" uid="uid://nfabwr5mgdir" path="res://assets/graphics/reference_images/scale.png" id="6_7v0q5"] -[node name="Reference Images" type="VBoxContainer"] +[sub_resource type="InputEventAction" id="InputEventAction_unp5j"] +action = &"move_frame_right" + +[sub_resource type="Shortcut" id="Shortcut_uucm4"] +events = [SubResource("InputEventAction_unp5j")] + +[sub_resource type="ButtonGroup" id="ButtonGroup_adw61"] + +[node name="Reference Images" type="PanelContainer"] +custom_minimum_size = Vector2(300, 0) anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 script = ExtResource("1") -[node name="Header" type="HBoxContainer" parent="."] +[node name="ScrollContainer" type="ScrollContainer" parent="."] layout_mode = 2 -theme_override_constants/separation = 0 -[node name="HSeparator2" type="HSeparator" parent="Header"] +[node name="Container" type="VBoxContainer" parent="ScrollContainer"] layout_mode = 2 size_flags_horizontal = 3 +size_flags_vertical = 3 -[node name="Label" type="Label" parent="Header"] +[node name="Header" type="HBoxContainer" parent="ScrollContainer/Container"] +layout_mode = 2 +theme_override_constants/separation = 0 + +[node name="Label" type="Label" parent="ScrollContainer/Container/Header"] layout_mode = 2 theme_type_variation = &"HeaderSmall" -text = "Reference Images" +text = "My Reference Images" + +[node name="Tip" type="Label" parent="ScrollContainer/Container"] +custom_minimum_size = Vector2(10, 0) +layout_mode = 2 +text = "When opening an image, it may be imported as a reference." +autowrap_mode = 2 + +[node name="Tools" type="HBoxContainer" parent="ScrollContainer/Container"] +layout_mode = 2 -[node name="HSeparator" type="HSeparator" parent="Header"] +[node name="MoveImageLeftBtn" type="Button" parent="ScrollContainer/Container/Tools" groups=["UIButtons"]] +unique_name_in_owner = true +custom_minimum_size = Vector2(31, 31) +layout_mode = 2 +size_flags_horizontal = 0 +tooltip_text = "Move the selected reference image to the left" +focus_mode = 0 +mouse_default_cursor_shape = 2 +shortcut = SubResource("Shortcut_uucm4") + +[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Container/Tools/MoveImageLeftBtn"] +layout_mode = 0 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -7.5 +offset_top = -5.5 +offset_right = 7.5 +offset_bottom = 5.5 +texture = ExtResource("2_uqbp6") +flip_h = true + +[node name="MoveImageRightBtn" type="Button" parent="ScrollContainer/Container/Tools" groups=["UIButtons"]] +unique_name_in_owner = true +custom_minimum_size = Vector2(31, 31) +layout_mode = 2 +size_flags_horizontal = 0 +tooltip_text = "Move the selected reference image to the right" +focus_mode = 0 +mouse_default_cursor_shape = 2 +shortcut = SubResource("Shortcut_uucm4") + +[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Container/Tools/MoveImageRightBtn"] +layout_mode = 0 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -7.5 +offset_top = -5.5 +offset_right = 7.5 +offset_bottom = 5.5 +texture = ExtResource("2_uqbp6") + +[node name="Spacer" type="Control" parent="ScrollContainer/Container/Tools"] layout_mode = 2 size_flags_horizontal = 3 -[node name="Label" type="Label" parent="."] +[node name="TransformTools" type="HBoxContainer" parent="ScrollContainer/Container/Tools"] layout_mode = 2 -text = "When opening an image, it may be imported as a reference." -[node name="Scroll" type="ScrollContainer" parent="."] +[node name="Select" type="Button" parent="ScrollContainer/Container/Tools/TransformTools" groups=["UIButtons"]] +custom_minimum_size = Vector2(31, 31) +layout_mode = 2 +size_flags_horizontal = 0 +tooltip_text = "Selects a reference image on the canvas" +focus_mode = 0 +mouse_default_cursor_shape = 2 +toggle_mode = true +button_pressed = true +button_group = SubResource("ButtonGroup_adw61") +shortcut = SubResource("Shortcut_uucm4") + +[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Container/Tools/TransformTools/Select"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -12.0 +offset_top = -12.0 +offset_right = 12.0 +offset_bottom = 12.0 +grow_horizontal = 2 +grow_vertical = 2 +texture = ExtResource("3_us8st") + +[node name="Move" type="Button" parent="ScrollContainer/Container/Tools/TransformTools" groups=["UIButtons"]] +custom_minimum_size = Vector2(31, 31) +layout_mode = 2 +size_flags_horizontal = 0 +tooltip_text = "Move the selected reference image" +focus_mode = 0 +mouse_default_cursor_shape = 2 +toggle_mode = true +button_group = SubResource("ButtonGroup_adw61") +shortcut = SubResource("Shortcut_uucm4") + +[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Container/Tools/TransformTools/Move"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -12.0 +offset_top = -12.0 +offset_right = 12.0 +offset_bottom = 12.0 +grow_horizontal = 2 +grow_vertical = 2 +texture = ExtResource("4_8mlcg") + +[node name="Rotate" type="Button" parent="ScrollContainer/Container/Tools/TransformTools" groups=["UIButtons"]] +custom_minimum_size = Vector2(31, 31) +layout_mode = 2 +size_flags_horizontal = 0 +tooltip_text = "Rotate the selected image" +focus_mode = 0 +mouse_default_cursor_shape = 2 +toggle_mode = true +button_group = SubResource("ButtonGroup_adw61") +shortcut = SubResource("Shortcut_uucm4") + +[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Container/Tools/TransformTools/Rotate"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -12.0 +offset_top = -12.0 +offset_right = 12.0 +offset_bottom = 12.0 +grow_horizontal = 2 +grow_vertical = 2 +texture = ExtResource("5_ifey7") + +[node name="Scale" type="Button" parent="ScrollContainer/Container/Tools/TransformTools" groups=["UIButtons"]] +custom_minimum_size = Vector2(31, 31) +layout_mode = 2 +size_flags_horizontal = 0 +tooltip_text = "Scale the selected reference image" +focus_mode = 0 +mouse_default_cursor_shape = 2 +toggle_mode = true +button_group = SubResource("ButtonGroup_adw61") +shortcut = SubResource("Shortcut_uucm4") + +[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Container/Tools/TransformTools/Scale"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -12.0 +offset_top = -12.0 +offset_right = 12.0 +offset_bottom = 12.0 +grow_horizontal = 2 +grow_vertical = 2 +texture = ExtResource("6_7v0q5") + +[node name="ReferenceImages" type="ScrollContainer" parent="ScrollContainer/Container"] +layout_mode = 2 +horizontal_scroll_mode = 2 +vertical_scroll_mode = 0 + +[node name="List" type="HBoxContainer" parent="ScrollContainer/Container/ReferenceImages"] +unique_name_in_owner = true +custom_minimum_size = Vector2(0, 64) +layout_mode = 2 + +[node name="ReferenceEdit" type="VBoxContainer" parent="ScrollContainer/Container"] +layout_mode = 2 +script = ExtResource("3_skjtb") + +[node name="ImageOptions" type="HBoxContainer" parent="ScrollContainer/Container/ReferenceEdit"] +layout_mode = 2 + +[node name="WarningLabel" type="Label" parent="ScrollContainer/Container/ReferenceEdit/ImageOptions"] layout_mode = 2 size_flags_horizontal = 3 -size_flags_vertical = 3 +theme_override_colors/font_color = Color(0.972549, 1, 0.545098, 1) +text = "Image not found!" +text_overrun_behavior = 3 -[node name="List" type="VBoxContainer" parent="Scroll"] +[node name="ImagePath" type="Button" parent="ScrollContainer/Container/ReferenceEdit/ImageOptions"] +visible = false layout_mode = 2 size_flags_horizontal = 3 -size_flags_vertical = 3 +size_flags_vertical = 4 +text = "Image Path" +alignment = 0 +text_overrun_behavior = 3 + +[node name="Remove" type="Button" parent="ScrollContainer/Container/ReferenceEdit/ImageOptions"] +layout_mode = 2 +tooltip_text = "Hold Shift while pressing to instantly remove." +theme_override_colors/font_color = Color(1, 0.490196, 0.419608, 1) +text = "Remove" + +[node name="Options" type="GridContainer" parent="ScrollContainer/Container/ReferenceEdit"] +layout_mode = 2 +size_flags_horizontal = 3 +columns = 2 + +[node name="TransformLabel" type="Label" parent="ScrollContainer/Container/ReferenceEdit/Options"] +layout_mode = 2 +text = "Transform" + +[node name="Reset" type="Button" parent="ScrollContainer/Container/ReferenceEdit/Options"] +layout_mode = 2 +text = "Reset Transform" + +[node name="PosLabel" type="Label" parent="ScrollContainer/Container/ReferenceEdit/Options"] +custom_minimum_size = Vector2(115, 0) +layout_mode = 2 +text = "Position" + +[node name="Position" type="HBoxContainer" parent="ScrollContainer/Container/ReferenceEdit/Options"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="X" parent="ScrollContainer/Container/ReferenceEdit/Options/Position" instance=ExtResource("3_1w6gu")] +layout_mode = 2 +allow_greater = true +allow_lesser = true + +[node name="Y" parent="ScrollContainer/Container/ReferenceEdit/Options/Position" instance=ExtResource("3_1w6gu")] +layout_mode = 2 +allow_greater = true +allow_lesser = true + +[node name="ScaleLabel" type="Label" parent="ScrollContainer/Container/ReferenceEdit/Options"] +custom_minimum_size = Vector2(115, 0) +layout_mode = 2 +text = "Scale" + +[node name="Scale" parent="ScrollContainer/Container/ReferenceEdit/Options" instance=ExtResource("3_1w6gu")] +layout_mode = 2 +allow_greater = true +allow_lesser = true + +[node name="RotationLabel" type="Label" parent="ScrollContainer/Container/ReferenceEdit/Options"] +custom_minimum_size = Vector2(115, 0) +layout_mode = 2 +text = "Rotation" + +[node name="Rotation" parent="ScrollContainer/Container/ReferenceEdit/Options" instance=ExtResource("3_1w6gu")] +layout_mode = 2 +min_value = -180.0 +max_value = 180.0 + +[node name="ColorLabel" type="Label" parent="ScrollContainer/Container/ReferenceEdit/Options"] +layout_mode = 2 +text = "Color Options" + +[node name="Spacer2" type="Control" parent="ScrollContainer/Container/ReferenceEdit/Options"] +layout_mode = 2 + +[node name="FilterLabel" type="Label" parent="ScrollContainer/Container/ReferenceEdit/Options"] +custom_minimum_size = Vector2(115, 0) +layout_mode = 2 +text = "Filter" + +[node name="Filter" type="CheckButton" parent="ScrollContainer/Container/ReferenceEdit/Options"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Enabled" + +[node name="MonochromeLabel" type="Label" parent="ScrollContainer/Container/ReferenceEdit/Options"] +custom_minimum_size = Vector2(115, 0) +layout_mode = 2 +text = "Monochrome" + +[node name="Monochrome" type="CheckButton" parent="ScrollContainer/Container/ReferenceEdit/Options"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Enabled" + +[node name="OverlayLabel" type="Label" parent="ScrollContainer/Container/ReferenceEdit/Options"] +custom_minimum_size = Vector2(115, 0) +layout_mode = 2 +text = "Overlay" + +[node name="Overlay" type="ColorPickerButton" parent="ScrollContainer/Container/ReferenceEdit/Options"] +custom_minimum_size = Vector2(48, 28) +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_stretch_ratio = 0.25 +edit_alpha = false + +[node name="OpacityLabel" type="Label" parent="ScrollContainer/Container/ReferenceEdit/Options"] +custom_minimum_size = Vector2(115, 0) +layout_mode = 2 +text = "Opacity" + +[node name="Opacity" parent="ScrollContainer/Container/ReferenceEdit/Options" instance=ExtResource("3_1w6gu")] +layout_mode = 2 + +[node name="ColorClampingLabel" type="Label" parent="ScrollContainer/Container/ReferenceEdit/Options"] +custom_minimum_size = Vector2(115, 0) +layout_mode = 2 +text = "Color Clamping" + +[node name="ColorClamping" type="TextureProgressBar" parent="ScrollContainer/Container/ReferenceEdit/Options"] +custom_minimum_size = Vector2(0, 24) +layout_mode = 2 +size_flags_horizontal = 3 +focus_mode = 2 +theme_type_variation = &"ValueSlider" +nine_patch_stretch = true +stretch_margin_left = 3 +stretch_margin_top = 3 +stretch_margin_right = 3 +stretch_margin_bottom = 3 +script = ExtResource("2_1qu4x") + +[node name="Timer" type="Timer" parent="ScrollContainer/Container/ReferenceEdit"] +wait_time = 0.2 +one_shot = true + +[node name="ConfirmRemoveDialog" type="ConfirmationDialog" parent="ScrollContainer/Container/ReferenceEdit"] +position = Vector2i(0, 36) +size = Vector2i(454, 106) +dialog_text = "Are you sure you want to remove this reference image? It will not be deleted from your file system." +dialog_autowrap = true + +[node name="Overlay" type="CanvasLayer" parent="."] + +[node name="DragHighlight" type="ColorRect" parent="Overlay"] +color = Color(0, 0.741176, 1, 0.501961) + +[connection signal="pressed" from="ScrollContainer/Container/Tools/MoveImageLeftBtn" to="." method="_on_move_image_left_pressed"] +[connection signal="pressed" from="ScrollContainer/Container/Tools/MoveImageRightBtn" to="." method="_on_move_image_right_pressed"] +[connection signal="pressed" from="ScrollContainer/Container/ReferenceEdit/ImageOptions/Remove" to="ScrollContainer/Container/ReferenceEdit" method="_on_Remove_pressed"] +[connection signal="pressed" from="ScrollContainer/Container/ReferenceEdit/Options/Reset" to="ScrollContainer/Container/ReferenceEdit" method="_on_Reset_pressed"] +[connection signal="value_changed" from="ScrollContainer/Container/ReferenceEdit/Options/Position/X" to="ScrollContainer/Container/ReferenceEdit" method="_on_X_value_changed"] +[connection signal="value_changed" from="ScrollContainer/Container/ReferenceEdit/Options/Position/Y" to="ScrollContainer/Container/ReferenceEdit" method="_on_Y_value_changed"] +[connection signal="value_changed" from="ScrollContainer/Container/ReferenceEdit/Options/Scale" to="ScrollContainer/Container/ReferenceEdit" method="_on_Scale_value_changed"] +[connection signal="value_changed" from="ScrollContainer/Container/ReferenceEdit/Options/Rotation" to="ScrollContainer/Container/ReferenceEdit" method="_on_Rotation_value_changed"] +[connection signal="toggled" from="ScrollContainer/Container/ReferenceEdit/Options/Filter" to="ScrollContainer/Container/ReferenceEdit" method="_on_Filter_toggled"] +[connection signal="toggled" from="ScrollContainer/Container/ReferenceEdit/Options/Monochrome" to="ScrollContainer/Container/ReferenceEdit" method="_on_Monochrome_toggled"] +[connection signal="color_changed" from="ScrollContainer/Container/ReferenceEdit/Options/Overlay" to="ScrollContainer/Container/ReferenceEdit" method="_on_Overlay_color_changed"] +[connection signal="value_changed" from="ScrollContainer/Container/ReferenceEdit/Options/Opacity" to="ScrollContainer/Container/ReferenceEdit" method="_on_Opacity_value_changed"] +[connection signal="value_changed" from="ScrollContainer/Container/ReferenceEdit/Options/ColorClamping" to="ScrollContainer/Container/ReferenceEdit" method="_on_ColorClamping_value_changed"] +[connection signal="timeout" from="ScrollContainer/Container/ReferenceEdit/Timer" to="ScrollContainer/Container/ReferenceEdit" method="_on_timer_timeout"] +[connection signal="canceled" from="ScrollContainer/Container/ReferenceEdit/ConfirmRemoveDialog" to="ScrollContainer/Container/ReferenceEdit" method="_on_confirm_remove_dialog_canceled"] +[connection signal="confirmed" from="ScrollContainer/Container/ReferenceEdit/ConfirmRemoveDialog" to="ScrollContainer/Container/ReferenceEdit" method="_on_confirm_remove_dialog_confirmed"] diff --git a/src/UI/UI.tscn b/src/UI/UI.tscn index 1801f7eae554..cc29f95bb815 100644 --- a/src/UI/UI.tscn +++ b/src/UI/UI.tscn @@ -10,7 +10,7 @@ [ext_resource type="Shader" path="res://src/Shaders/Greyscale.gdshader" id="8"] [ext_resource type="Shader" path="res://src/Shaders/TransparentChecker.gdshader" id="9"] [ext_resource type="PackedScene" uid="uid://wo0hqxkst808" path="res://src/UI/GlobalToolOptions/GlobalToolOptions.tscn" id="10"] -[ext_resource type="PackedScene" uid="uid://b75xk2ix8xxml" path="res://src/UI/ReferenceImages/ReferencesPanel.tscn" id="11"] +[ext_resource type="PackedScene" uid="uid://cxhs8qy5ilufv" path="res://src/UI/ReferenceImages/ReferencesPanel.tscn" id="11"] [ext_resource type="PackedScene" uid="uid://cap1bhavhi33g" path="res://src/UI/PerspectiveEditor/PerspectiveEditor.tscn" id="12"] [ext_resource type="PackedScene" uid="uid://dl6ook010q86o" path="res://src/UI/Recorder/Recorder.tscn" id="13"] [ext_resource type="Script" path="res://addons/dockable_container/layout.gd" id="14"]