1
0
Fork 0
mirror of https://github.com/Orama-Interactive/Pixelorama.git synced 2025-01-19 09:39:48 +00:00
Pixelorama/src/Autoload/Tools.gd

351 lines
11 KiB
GDScript3
Raw Normal View History

extends Node
signal color_changed(color, button)
var pen_pressure := 1.0
var horizontal_mirror := false
var vertical_mirror := false
var control := false
var shift := false
var alt := false
var tools := {
"RectSelect":
Tool.new("RectSelect", "Rectangular Selection", "rectangle_select", "", [], "SelectionTools"),
"EllipseSelect":
Tool.new("EllipseSelect", "Elliptical Selection", "ellipse_select", "", [], "SelectionTools"),
"PolygonSelect":
Tool.new(
"PolygonSelect",
"Polygonal Selection",
"polygon_select",
"Double-click to connect the last point to the starting point",
[],
"SelectionTools"
),
"ColorSelect":
Tool.new("ColorSelect", "Select By Color", "color_select", "", [], "SelectionTools"),
"MagicWand": Tool.new("MagicWand", "Magic Wand", "magic_wand", "", [], "SelectionTools"),
"Lasso": Tool.new("Lasso", "Lasso / Free Select Tool", "lasso", "", [], "SelectionTools"),
"Move": Tool.new("Move", "Move", "move"),
"Zoom": Tool.new("Zoom", "Zoom", "zoom"),
"Pan": Tool.new("Pan", "Pan", "pan"),
"ColorPicker":
Tool.new(
"ColorPicker", "Color Picker", "colorpicker", "Select a color from a pixel of the sprite"
),
"Pencil": Tool.new("Pencil", "Pencil", "pencil", "Hold %s to make a line", ["Shift"]),
"Eraser": Tool.new("Eraser", "Eraser", "eraser", "Hold %s to make a line", ["Shift"]),
"Bucket": Tool.new("Bucket", "Bucket", "fill"),
"Shading": Tool.new("Shading", "Shading Tool", "shading"),
"LineTool":
Tool.new(
"LineTool",
"Line Tool",
"linetool",
"""Hold %s to snap the angle of the line
Hold %s to center the shape on the click origin
Hold %s to displace the shape's origin""",
["Shift", "Ctrl", "Alt"]
),
"RectangleTool":
Tool.new(
"RectangleTool",
"Rectangle Tool",
"rectangletool",
"""Hold %s to create a 1:1 shape
Hold %s to center the shape on the click origin
Hold %s to displace the shape's origin""",
["Shift", "Ctrl", "Alt"]
),
"EllipseTool":
Tool.new(
"EllipseTool",
"Ellipse Tool",
"ellipsetool",
"""Hold %s to create a 1:1 shape
Hold %s to center the shape on the click origin
Hold %s to displace the shape's origin""",
["Shift", "Ctrl", "Alt"]
),
}
var _tool_button_scene: PackedScene = preload("res://src/Tools/ToolButton.tscn")
var _slots := {}
var _panels := {}
var _tool_buttons: Node
var _active_button := -1
var _last_position := Vector2.INF
class Tool:
var name := ""
var display_name := ""
var scene: PackedScene
var icon: Texture
var shortcut := ""
var extra_hint := ""
var extra_shortcuts := [] # Array of String(s)
var button_node: BaseButton
func _init(
_name: String,
_display_name: String,
_shortcut: String,
_extra_hint := "",
_extra_shortucts := [],
subdir := ""
) -> void:
name = _name
display_name = _display_name
shortcut = _shortcut
extra_hint = _extra_hint
extra_shortcuts = _extra_shortucts
icon = load("res://assets/graphics/tools/%s.png" % name.to_lower())
if subdir.empty():
scene = load("res://src/Tools/%s.tscn" % name)
else:
scene = load("res://src/Tools/%s/%s.tscn" % [subdir, name])
func generate_hint_tooltip() -> String:
var left_shortcut: String = InputMap.get_action_list("left_" + shortcut + "_tool")[0].as_text()
var right_shortcut: String = InputMap.get_action_list("right_" + shortcut + "_tool")[0].as_text()
var hint := display_name
hint += "\n\n%s for left mouse button\n%s for right mouse button"
if !extra_hint.empty():
hint += "\n\n" + extra_hint
var shortcuts := [left_shortcut, right_shortcut]
shortcuts.append_array(extra_shortcuts)
hint = tr(hint) % shortcuts
return hint
class Slot:
var name: String
var kname: String
var tool_node: Node = null
var button: int
var color: Color
var pixel_perfect := false
func _init(slot_name: String) -> void:
name = slot_name
kname = name.replace(" ", "_").to_lower()
load_config()
func save_config() -> void:
var config := {
"pixel_perfect": pixel_perfect,
}
Global.config_cache.set_value(kname, "slot", config)
func load_config() -> void:
var config = Global.config_cache.get_value(kname, "slot", {})
pixel_perfect = config.get("pixel_perfect", pixel_perfect)
func _ready() -> void:
_tool_buttons = Global.control.find_node("ToolButtons")
for t in tools:
add_tool_button(tools[t])
_slots[BUTTON_LEFT] = Slot.new("Left tool")
_slots[BUTTON_RIGHT] = Slot.new("Right tool")
_panels[BUTTON_LEFT] = Global.control.find_node("LeftPanelContainer", true, false)
_panels[BUTTON_RIGHT] = Global.control.find_node("RightPanelContainer", true, false)
var tool_name: String = Global.config_cache.get_value(
_slots[BUTTON_LEFT].kname, "tool", "Pencil"
)
if not tool_name in tools:
tool_name = "Pencil"
set_tool(tool_name, BUTTON_LEFT)
tool_name = Global.config_cache.get_value(_slots[BUTTON_RIGHT].kname, "tool", "Eraser")
if not tool_name in tools:
tool_name = "Eraser"
set_tool(tool_name, BUTTON_RIGHT)
update_tool_buttons()
update_tool_cursors()
horizontal_mirror = Global.config_cache.get_value("preferences", "horizontal_mirror", false)
vertical_mirror = Global.config_cache.get_value("preferences", "vertical_mirror", false)
# Yield is necessary for the color picker nodes to update their color values
yield(get_tree(), "idle_frame")
var color_value: Color = Global.config_cache.get_value(
_slots[BUTTON_LEFT].kname, "color", Color.black
)
assign_color(color_value, BUTTON_LEFT, false)
color_value = Global.config_cache.get_value(_slots[BUTTON_RIGHT].kname, "color", Color.white)
assign_color(color_value, BUTTON_RIGHT, false)
func add_tool_button(t: Tool) -> void:
var tool_button: BaseButton = _tool_button_scene.instance()
tool_button.name = t.name
tool_button.get_node("ToolIcon").texture = t.icon
t.button_node = tool_button
_tool_buttons.add_child(tool_button)
tool_button.connect("pressed", _tool_buttons, "_on_Tool_pressed", [tool_button])
func set_tool(name: String, button: int) -> void:
var slot = _slots[button]
var panel: Node = _panels[button]
var node: Node = tools[name].scene.instance()
if button == BUTTON_LEFT: # As guides are only moved with left mouse
if name == "Pan": # tool you want to give more access at guides
Global.move_guides_on_canvas = true
else:
Global.move_guides_on_canvas = false
node.name = name
node.tool_slot = slot
slot.tool_node = node
slot.button = button
panel.add_child(slot.tool_node)
func assign_tool(name: String, button: int) -> void:
var slot = _slots[button]
var panel: Node = _panels[button]
if slot.tool_node != null:
if slot.tool_node.name == name:
return
panel.remove_child(slot.tool_node)
slot.tool_node.queue_free()
set_tool(name, button)
update_tool_buttons()
update_tool_cursors()
Global.config_cache.set_value(slot.kname, "tool", name)
func default_color() -> void:
assign_color(Color.black, BUTTON_LEFT)
assign_color(Color.white, BUTTON_RIGHT)
func swap_color() -> void:
var left = _slots[BUTTON_LEFT].color
var right = _slots[BUTTON_RIGHT].color
assign_color(right, BUTTON_LEFT, false)
assign_color(left, BUTTON_RIGHT, false)
func assign_color(color: Color, button: int, change_alpha := true) -> void:
var c: Color = _slots[button].color
# This was requested by Issue #54 on GitHub
if color.a == 0 and change_alpha:
if color.r != c.r or color.g != c.g or color.b != c.b:
color.a = 1
_slots[button].color = color
Global.config_cache.set_value(_slots[button].kname, "color", color)
emit_signal("color_changed", color, button)
func get_assigned_color(button: int) -> Color:
return _slots[button].color
func set_button_size(button_size: int) -> void:
if button_size == Global.ButtonSize.SMALL:
for t in _tool_buttons.get_children():
t.rect_min_size = Vector2(24, 24)
t.get_node("BackgroundLeft").rect_size.x = 12
t.get_node("BackgroundRight").rect_size.x = 12
t.get_node("BackgroundRight").rect_position = Vector2(24, 24)
else:
for t in _tool_buttons.get_children():
t.rect_min_size = Vector2(32, 32)
t.get_node("BackgroundLeft").rect_size.x = 16
t.get_node("BackgroundRight").rect_size.x = 16
t.get_node("BackgroundRight").rect_position = Vector2(32, 32)
func update_tool_buttons() -> void:
for child in _tool_buttons.get_children():
var left_background: NinePatchRect = child.get_node("BackgroundLeft")
var right_background: NinePatchRect = child.get_node("BackgroundRight")
left_background.visible = _slots[BUTTON_LEFT].tool_node.name == child.name
right_background.visible = _slots[BUTTON_RIGHT].tool_node.name == child.name
func update_hint_tooltips() -> void:
for tool_name in tools:
var t: Tool = tools[tool_name]
t.button_node.hint_tooltip = t.generate_hint_tooltip()
func update_tool_cursors() -> void:
var left_image = load(
"res://assets/graphics/tools/cursors/%s.png" % _slots[BUTTON_LEFT].tool_node.name.to_lower()
)
Global.left_cursor.texture = left_image
var right_image = load(
(
"res://assets/graphics/tools/cursors/%s.png"
% _slots[BUTTON_RIGHT].tool_node.name.to_lower()
)
)
Global.right_cursor.texture = right_image
func draw_indicator() -> void:
if Global.left_square_indicator_visible:
_slots[BUTTON_LEFT].tool_node.draw_indicator()
if Global.right_square_indicator_visible:
_slots[BUTTON_RIGHT].tool_node.draw_indicator()
func draw_preview() -> void:
_slots[BUTTON_LEFT].tool_node.draw_preview()
_slots[BUTTON_RIGHT].tool_node.draw_preview()
func handle_draw(position: Vector2, event: InputEvent) -> void:
if not (Global.can_draw and Global.has_focus):
return
2021-03-29 20:00:09 +00:00
var draw_pos := position
if Global.mirror_view:
draw_pos.x = Global.current_project.size.x - position.x - 1
if event is InputEventWithModifiers:
control = event.control
shift = event.shift
alt = event.alt
if event is InputEventMouseButton:
if event.button_index in [BUTTON_LEFT, BUTTON_RIGHT]:
if event.pressed and _active_button == -1:
_active_button = event.button_index
2021-03-29 20:00:09 +00:00
_slots[_active_button].tool_node.draw_start(draw_pos)
elif not event.pressed and event.button_index == _active_button:
2021-03-29 20:00:09 +00:00
_slots[_active_button].tool_node.draw_end(draw_pos)
_active_button = -1
if event is InputEventMouseMotion:
pen_pressure = event.pressure
if Global.pressure_sensitivity_mode == Global.PressureSensitivity.NONE:
pen_pressure = 1.0
if not position.is_equal_approx(_last_position):
_last_position = position
_slots[BUTTON_LEFT].tool_node.cursor_move(position)
_slots[BUTTON_RIGHT].tool_node.cursor_move(position)
if _active_button != -1:
2021-03-29 20:00:09 +00:00
_slots[_active_button].tool_node.draw_move(draw_pos)
var project: Project = Global.current_project
var text := "[%s×%s]" % [project.size.x, project.size.y]
if Global.has_focus:
text += " %s, %s" % [position.x, position.y]
if not _slots[BUTTON_LEFT].tool_node.cursor_text.empty():
text += " %s" % _slots[BUTTON_LEFT].tool_node.cursor_text
if not _slots[BUTTON_RIGHT].tool_node.cursor_text.empty():
text += " %s" % _slots[BUTTON_RIGHT].tool_node.cursor_text
Global.cursor_position_label.text = text