1
0
Fork 0
mirror of https://github.com/Orama-Interactive/Pixelorama.git synced 2025-02-01 07:59:48 +00:00
Pixelorama/src/UI/Nodes/GradientEdit.gd

201 lines
5.7 KiB
GDScript3
Raw Normal View History

# Code taken and modified from Material Maker, licensed under MIT
# gdlint: ignore=max-line-length
# https://github.com/RodZill4/material-maker/blob/master/material_maker/widgets/gradient_editor/gradient_editor.gd
class_name GradientEditNode
extends Control
signal updated(gradient, cc)
var continuous_change := true
var active_cursor: GradientCursor # Showing a color picker popup to change a cursor's color
onready var x_offset: float = rect_size.x - GradientCursor.WIDTH
onready var texture_rect: TextureRect = $TextureRect
onready var texture: Texture = $TextureRect.texture
onready var gradient: Gradient = texture.gradient
onready var color_picker: ColorPicker = $Popup.get_node("ColorPicker")
onready var divide_dialog: ConfirmationDialog = $DivideConfirmationDialog
onready var number_of_parts_spin_box: SpinBox = $"%NumberOfPartsSpinBox"
onready var add_point_end_check_box: CheckBox = $"%AddPointEndCheckBox"
class GradientCursor:
extends Control
const WIDTH := 10
var color: Color
var sliding := false
onready var parent: TextureRect = get_parent()
onready var grand_parent: Container = parent.get_parent()
onready var label: Label = parent.get_node("Value")
func _ready() -> void:
rect_position = Vector2(0, 15)
rect_size = Vector2(WIDTH, 15)
func _draw() -> void:
# warning-ignore:integer_division
var polygon := PoolVector2Array(
[
Vector2(0, 5),
Vector2(WIDTH / 2, 0),
Vector2(WIDTH, 5),
Vector2(WIDTH, 15),
Vector2(0, 15),
Vector2(0, 5)
]
)
var c := color
c.a = 1.0
draw_colored_polygon(polygon, c)
draw_polyline(polygon, Color(0.0, 0.0, 0.0) if color.v > 0.5 else Color(1.0, 1.0, 1.0))
func _gui_input(ev: InputEvent) -> void:
if ev is InputEventMouseButton:
if ev.button_index == BUTTON_LEFT:
if ev.doubleclick:
grand_parent.select_color(self, ev.global_position)
elif ev.pressed:
grand_parent.continuous_change = false
sliding = true
label.visible = true
label.text = "%.03f" % get_cursor_position()
else:
sliding = false
label.visible = false
elif ev.button_index == BUTTON_RIGHT and grand_parent.get_sorted_cursors().size() > 2:
parent.remove_child(self)
grand_parent.continuous_change = false
grand_parent.update_from_value()
queue_free()
elif ev is InputEventMouseMotion and (ev.button_mask & BUTTON_MASK_LEFT) != 0 and sliding:
rect_position.x += get_local_mouse_position().x
if ev.control:
rect_position.x = (
round(get_cursor_position() * 20.0)
* 0.05
* (parent.rect_size.x - WIDTH)
)
rect_position.x = min(max(0, rect_position.x), parent.rect_size.x - rect_size.x)
grand_parent.update_from_value()
label.text = "%.03f" % get_cursor_position()
func get_cursor_position() -> float:
return rect_position.x / (parent.rect_size.x - WIDTH)
func set_color(c: Color) -> void:
color = c
grand_parent.update_from_value()
update()
static func sort(a, b) -> bool:
return a.get_position() < b.get_position()
func can_drop_data(_position, data) -> bool:
return typeof(data) == TYPE_COLOR
func drop_data(_position, data) -> void:
set_color(data)
func _ready() -> void:
_create_cursors()
func _create_cursors() -> void:
for c in texture_rect.get_children():
if c is GradientCursor:
texture_rect.remove_child(c)
c.queue_free()
for i in gradient.get_point_count():
var p: float = gradient.get_offset(i)
add_cursor(p * x_offset, gradient.get_color(i))
func _gui_input(ev: InputEvent) -> void:
if ev.is_action_pressed("left_mouse"):
var p = clamp(ev.position.x, 0, x_offset)
add_cursor(p, get_gradient_color(p))
continuous_change = false
update_from_value()
func update_from_value() -> void:
gradient.offsets = []
for c in texture_rect.get_children():
if c is GradientCursor:
var point: float = c.rect_position.x / x_offset
gradient.add_point(point, c.color)
emit_signal("updated", gradient, continuous_change)
continuous_change = true
func add_cursor(x: float, color: Color) -> void:
var cursor := GradientCursor.new()
texture_rect.add_child(cursor)
cursor.rect_position.x = x
cursor.color = color
func select_color(cursor: GradientCursor, position: Vector2) -> void:
active_cursor = cursor
color_picker.color = cursor.color
if position.x > rect_global_position.x + (rect_size.x / 2.0):
position.x = rect_global_position.x + rect_size.x
else:
position.x = rect_global_position.x - $Popup.rect_size.x
$Popup.rect_position = position
$Popup.popup()
func get_sorted_cursors() -> Array:
var array := []
for c in texture_rect.get_children():
if c is GradientCursor:
array.append(c)
array.sort_custom(GradientCursor, "sort")
return array
func get_gradient_color(x: float) -> Color:
return gradient.interpolate(x / x_offset)
func _on_ColorPicker_color_changed(color: Color) -> void:
active_cursor.set_color(color)
func _on_GradientEdit_resized() -> void:
if not gradient:
return
x_offset = rect_size.x - GradientCursor.WIDTH
_create_cursors()
func _on_InterpolationOptionButton_item_selected(index: int) -> void:
gradient.interpolation_mode = index
func _on_DivideButton_pressed() -> void:
divide_dialog.popup_centered()
func _on_DivideConfirmationDialog_confirmed() -> void:
var add_point_to_end := add_point_end_check_box.pressed
var parts := number_of_parts_spin_box.value
var colors := []
var end_point = 1 if add_point_to_end else 0
parts -= end_point
if not add_point_to_end:
# Move the final color one part behind, useful for it to be in constant interpolation
gradient.add_point((parts - 1) / parts, gradient.interpolate(1))
for i in parts + end_point:
colors.append(gradient.interpolate(i / parts))
gradient.offsets = []
for i in parts + end_point:
gradient.add_point(i / parts, colors[i])
_create_cursors()
emit_signal("updated", gradient, continuous_change)