370 lines
9.7 KiB
GDScript
370 lines
9.7 KiB
GDScript
@tool
|
|
class_name EditingTool
|
|
extends VBoxContainer
|
|
|
|
enum {
|
|
TOOL_PROP_BOOL,
|
|
TOOL_PROP_INT,
|
|
TOOL_PROP_FLOAT,
|
|
TOOL_PROP_ENUM,
|
|
TOOL_PROP_ICON_ENUM,
|
|
TOOL_PROP_ICON_FLAGS,
|
|
TOOL_PROP_RESOURCE,
|
|
TOOL_PROP_FOLDER_SCAN,
|
|
TOOL_PROP_COLOR,
|
|
}
|
|
|
|
enum {
|
|
OPERATION_REPLACE,
|
|
OPERATION_ADD,
|
|
OPERATION_SUBTRACT,
|
|
OPERATION_INTERSECTION,
|
|
OPERATION_XOR,
|
|
}
|
|
|
|
@export var tool_name := "Box Selection"
|
|
@export_multiline var tool_desc := ""
|
|
@export var preview_shader : ShaderMaterial
|
|
@export_enum("None", "When Drawing", "When Active") var image_hide_mode := 0
|
|
|
|
var selection : BitMap
|
|
|
|
var _last_grid : GridContainer
|
|
var _hotkey_adjustment_hook : Callable
|
|
|
|
|
|
func _enter_tree():
|
|
if !visibility_changed.is_connected(_on_visibility_changed):
|
|
visibility_changed.connect(_on_visibility_changed)
|
|
|
|
set_process_shortcut_input(false)
|
|
|
|
|
|
func add_name():
|
|
var label = Label.new()
|
|
label.text = tool_name
|
|
label.size_flags_vertical = SIZE_SHRINK_CENTER | SIZE_FILL
|
|
add_child(label)
|
|
add_separator()
|
|
label = Label.new()
|
|
label.autowrap_mode = TextServer.AUTOWRAP_WORD
|
|
label.self_modulate.a = 0.75
|
|
label.text = tool_desc
|
|
label.size_flags_vertical = SIZE_SHRINK_CENTER | SIZE_FILL
|
|
if tool_desc == "": label.hide()
|
|
add_child(label)
|
|
|
|
_last_grid = null
|
|
|
|
|
|
func add_separator():
|
|
var sep = ColorRect.new()
|
|
sep.custom_minimum_size = Vector2(0, 2)
|
|
sep.color = get_theme_color("accent_color", "Editor")
|
|
sep.size_flags_horizontal = SIZE_EXPAND_FILL
|
|
add_child(sep)
|
|
|
|
_last_grid = null
|
|
|
|
|
|
func start_property_grid():
|
|
var grid = GridContainer.new()
|
|
grid.columns = 2
|
|
_last_grid = grid
|
|
add_child(grid)
|
|
return grid
|
|
|
|
|
|
func add_property(
|
|
property_name,
|
|
default_value,
|
|
setter : Callable,
|
|
type : int,
|
|
hint : Variant = null,
|
|
hotkey_adjustment = false,
|
|
):
|
|
var parent = _last_grid
|
|
if _last_grid == null:
|
|
parent = HBoxContainer.new()
|
|
add_child(parent)
|
|
|
|
var label = Label.new()
|
|
label.size_flags_vertical = SIZE_SHRINK_CENTER | SIZE_FILL
|
|
label.text = property_name
|
|
parent.add_child(label)
|
|
|
|
var editor
|
|
match type:
|
|
TOOL_PROP_BOOL:
|
|
editor = CheckBox.new()
|
|
editor.button_pressed = default_value
|
|
editor.text = "On"
|
|
editor.toggled.connect(setter)
|
|
if hotkey_adjustment:
|
|
_hotkey_adjustment_hook = func(x):
|
|
editor.button_pressed = !editor.button_pressed
|
|
setter.call(editor.button_pressed)
|
|
|
|
TOOL_PROP_INT, TOOL_PROP_FLOAT:
|
|
editor = EditorSpinSlider.new()
|
|
if hint == null:
|
|
editor.max_value = INF
|
|
editor.min_value = -INF
|
|
|
|
else:
|
|
editor.min_value = hint[0]
|
|
editor.max_value = hint[1]
|
|
editor.hide_slider = false
|
|
|
|
editor.value = default_value
|
|
editor.step = 0.01 if type == TOOL_PROP_FLOAT else 1.0
|
|
editor.custom_minimum_size.x = 64.0
|
|
editor.value_changed.connect(setter)
|
|
if hotkey_adjustment:
|
|
_hotkey_adjustment_hook = func(x):
|
|
editor.value += editor.step * x
|
|
setter.call(editor.value)
|
|
|
|
TOOL_PROP_ENUM:
|
|
if hint.size() != 2:
|
|
editor = OptionButton.new()
|
|
for x in hint:
|
|
editor.add_item(x)
|
|
|
|
editor.item_selected.connect(setter)
|
|
editor.select(default_value)
|
|
if hotkey_adjustment:
|
|
_hotkey_adjustment_hook = func(x):
|
|
editor.select(posmod((editor.get_selected_id() + x), hint.size()))
|
|
setter.call(editor.get_selected_id())
|
|
|
|
else:
|
|
editor = Button.new()
|
|
editor.text = hint[default_value]
|
|
editor.pressed.connect(func():
|
|
var x = 1 if editor.text == hint[0] else 0
|
|
editor.text = hint[x]
|
|
setter.call(x)
|
|
)
|
|
if hotkey_adjustment:
|
|
_hotkey_adjustment_hook = func(x):
|
|
var new_value = 1 if editor.text == hint[0] else 0
|
|
editor.text = hint[new_value]
|
|
setter.call(new_value)
|
|
|
|
|
|
TOOL_PROP_ICON_ENUM, TOOL_PROP_ICON_FLAGS:
|
|
editor = HBoxContainer.new()
|
|
var icons = hint if hint is Array else hint.keys()
|
|
var tooltips = {} if hint is Array else hint
|
|
var button
|
|
|
|
var bgroup = ButtonGroup.new()
|
|
|
|
for x in icons:
|
|
button = load("res://addons/sprite_painter/editor_icon_button.gd").new()
|
|
button.tooltip_text = tooltips.get(x)
|
|
button.toggle_mode = true
|
|
button.add_theme_stylebox_override("pressed", button.get_theme_stylebox("focus", "Button"))
|
|
button.add_theme_stylebox_override("focus", StyleBoxEmpty.new())
|
|
|
|
if x is Texture:
|
|
button.set_deferred("icon", x)
|
|
|
|
else:
|
|
button._set_icon_name(x)
|
|
|
|
editor.add_child(button)
|
|
if type == TOOL_PROP_ICON_ENUM:
|
|
button.button_group = bgroup
|
|
button.toggled.connect(func(toggled):
|
|
if !toggled: return
|
|
setter.call(button.get_index())
|
|
)
|
|
button.button_pressed = default_value == button.get_index()
|
|
|
|
else:
|
|
button.toggled.connect(func(toggled):
|
|
default_value[button.get_index()] = toggled
|
|
setter.call(default_value)
|
|
)
|
|
button.button_pressed = default_value[button.get_index()]
|
|
|
|
if hotkey_adjustment:
|
|
_hotkey_adjustment_hook = func(x):
|
|
var new_value : int
|
|
for i in editor.get_child_count():
|
|
if editor.get_child(i).button_pressed:
|
|
new_value = posmod(i + x, hint.size())
|
|
editor.get_child(new_value).button_pressed = true
|
|
editor.get_child(i).button_pressed = false
|
|
break
|
|
|
|
# setter.call(new_value)
|
|
|
|
TOOL_PROP_RESOURCE:
|
|
editor = EditorResourcePicker.new()
|
|
editor.resource_changed.connect(func(x):
|
|
setter.call(x)
|
|
)
|
|
editor.edited_resource = default_value
|
|
if !hint is Array || hint.size() > 0:
|
|
editor.base_type = hint if hint is String else hint[0]
|
|
|
|
var plugin_root = get_parent()
|
|
while !plugin_root is Window:
|
|
plugin_root = plugin_root.get_parent()
|
|
if plugin_root is SpritePainterRoot:
|
|
editor.resource_selected.connect(func(x, inspected):
|
|
plugin_root.editor_interface.edit_resource(x)
|
|
)
|
|
break
|
|
|
|
TOOL_PROP_FOLDER_SCAN:
|
|
editor = OptionButton.new()
|
|
var folder = (hint if hint is String else hint[0]).trim_suffix("/") + "/"
|
|
var filenames = DirAccess.get_files_at(folder)
|
|
|
|
for x in filenames:
|
|
editor.add_item(x.get_basename())
|
|
|
|
editor.item_selected.connect(func(x):
|
|
setter.call(load(folder + filenames[x]))
|
|
)
|
|
if default_value is int:
|
|
editor.select(default_value)
|
|
|
|
elif default_value is String:
|
|
default_value = default_value.get_file()
|
|
|
|
var found_index = filenames.find(default_value)
|
|
editor.select(found_index)
|
|
|
|
if hotkey_adjustment:
|
|
_hotkey_adjustment_hook = func(x):
|
|
editor.select(posmod((editor.get_selected_id() + x), hint.size()))
|
|
setter.call(load(folder + filenames[editor.get_selected_id()]))
|
|
|
|
TOOL_PROP_COLOR:
|
|
editor = ColorPickerButton.new()
|
|
editor.color = default_value
|
|
editor.color_changed.connect(setter)
|
|
|
|
|
|
editor.size_flags_horizontal = SIZE_EXPAND_FILL
|
|
parent.add_child(editor)
|
|
return editor
|
|
|
|
|
|
func add_text_display():
|
|
var textbox = LineEdit.new()
|
|
textbox.editable = false
|
|
textbox.size_flags_vertical = SIZE_FILL
|
|
textbox.expand_to_text_length = true
|
|
add_child(textbox)
|
|
|
|
_last_grid = null
|
|
return textbox
|
|
|
|
|
|
func add_button_panel(labels : Array[String]):
|
|
var container = HFlowContainer.new()
|
|
for x in labels:
|
|
var new_button = Button.new()
|
|
new_button.text = x
|
|
new_button.size_flags_horizontal = SIZE_EXPAND_FILL
|
|
container.add_child(new_button)
|
|
|
|
add_child(container)
|
|
|
|
_last_grid = null
|
|
return container
|
|
|
|
|
|
func is_out_of_bounds(pos : Vector2i, rect_size : Vector2i):
|
|
return (
|
|
pos.x < 0 || pos.y < 0
|
|
|| pos.x >= rect_size.x || pos.y >= rect_size.y
|
|
)
|
|
|
|
|
|
func get_rect_from_drag(start_pos, end_pos, squarify : bool = false):
|
|
var rect = Rect2i(start_pos, end_pos - start_pos)
|
|
if !squarify:
|
|
return rect.abs().grow_individual(0, 0, 1, 1)
|
|
|
|
var max_side = maxi(abs(rect.size.x), abs(rect.size.y))
|
|
var square = Rect2i(rect.position, Vector2i(max_side, max_side))
|
|
if rect.size.x < 0:
|
|
square.position.x -= square.size.x
|
|
|
|
if rect.size.y < 0:
|
|
square.position.y -= square.size.y
|
|
|
|
return square
|
|
|
|
|
|
|
|
func is_selection_empty():
|
|
return selection.get_true_bit_count() == selection.get_size().x * selection.get_size().y
|
|
|
|
|
|
func set_image_pixel(image : Image, x : int, y : int, color : Color):
|
|
if !is_out_of_bounds(Vector2i(x, y), image.get_size()):
|
|
if selection.get_bit(x, y):
|
|
image.set_pixel(x, y, color)
|
|
|
|
|
|
func set_image_pixelv(image : Image, pos : Vector2i, color : Color):
|
|
if !is_out_of_bounds(pos, image.get_size()):
|
|
if selection.get_bitv(pos):
|
|
image.set_pixelv(pos, color)
|
|
|
|
|
|
func mouse_pressed(
|
|
event : InputEventMouseButton,
|
|
image : Image,
|
|
color1 : Color = Color.BLACK,
|
|
color2 : Color = Color.WHITE,
|
|
):
|
|
printerr("Not implemented: mouse_pressed! (" + get_script().resource_path.get_file() + ")")
|
|
|
|
|
|
func get_affected_rect() -> Rect2i:
|
|
printerr("Not implemented: get_affected_rect! (" + get_script().resource_path.get_file() + ")")
|
|
return Rect2i()
|
|
|
|
|
|
func mouse_moved(event : InputEventMouseMotion):
|
|
printerr("Not implemented: mouse_moved! (" + get_script().resource_path.get_file() + ")")
|
|
|
|
|
|
func draw_preview(image_view : CanvasItem, mouse_position : Vector2i):
|
|
printerr("Not implemented: draw_preview! (" + get_script().resource_path.get_file() + ")")
|
|
|
|
|
|
func draw_shader_preview(image_view : CanvasItem, mouse_position : Vector2i):
|
|
pass
|
|
|
|
|
|
func draw_crosshair(image_view : CanvasItem, mouse_position : Vector2i, line_length : int, color : Color):
|
|
image_view.draw_rect(Rect2i(mouse_position + Vector2i(0, 4), Vector2(1, +line_length)).abs(), color)
|
|
image_view.draw_rect(Rect2i(mouse_position - Vector2i(0, 3), Vector2(1, -line_length)).abs(), color)
|
|
image_view.draw_rect(Rect2i(mouse_position + Vector2i(4, 0), Vector2(+line_length, 1)).abs(), color)
|
|
image_view.draw_rect(Rect2i(mouse_position - Vector2i(3, 0), Vector2(-line_length, 1)).abs(), color)
|
|
|
|
|
|
func _shortcut_input(event : InputEvent):
|
|
if _hotkey_adjustment_hook.is_null(): return
|
|
if !event is InputEventKey: return
|
|
if !event.pressed: return
|
|
if event.keycode != KEY_BRACKETLEFT:
|
|
_hotkey_adjustment_hook.call(+1)
|
|
|
|
if event.keycode != KEY_BRACKETRIGHT:
|
|
_hotkey_adjustment_hook.call(-1)
|
|
|
|
|
|
func _on_visibility_changed():
|
|
set_process_shortcut_input(visible)
|