2026-03-19 11:35:38 -05:00

338 lines
15 KiB
GDScript

# toolbar.gd
# This file is part of: SimpleGrassTextured
# Copyright (c) 2023 IcterusGames
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@tool
extends Control
enum MENU_SHAPE_ID {
TOOL_SHAPE_SPHERE,
TOOL_SHAPE_CYLINDER,
TOOL_SHAPE_CYLINDER_INF_H,
TOOL_SHAPE_BOX,
TOOL_SHAPE_BOX_INF_H,
}
const DEFAULT_RADIUS := 2.0
const DEFAULT_DENSITY := 25.0
const DEFAULT_SCALE := 1.0
const DEFAULT_ROTATION := 0.0
const DEFAULT_ROTATION_RAND := 1.0
const DEFAULT_DISTANCE := 0.25
var _shortcut_radius_inc := Shortcut.new()
var _shortcut_radius_dec := Shortcut.new()
var _shortcut_density_inc := Shortcut.new()
var _shortcut_density_dec := Shortcut.new()
var _plugin: EditorPlugin = null
var _grass_selected = null
@onready var button_airbrush :Button = %ButtonAirbrush
@onready var button_pencil :Button = %ButtonPencil
@onready var button_eraser :Button = %ButtonEraser
@onready var button_density :Button = %IconDensity
@onready var slider_radius :HSlider = $HSliderRadius
@onready var slider_density :HSlider = $HSliderDensity
@onready var edit_slope_range: Control = %SlopeRange
@onready var edit_scale :EditorSpinSlider
@onready var edit_rotation :EditorSpinSlider
@onready var edit_rotation_rand :EditorSpinSlider
@onready var edit_distance :EditorSpinSlider
@onready var label_stats :Label = $LabelStats
@onready var _label_radius :Label = $HSliderRadius/Label
@onready var _label_density :Label = $HSliderDensity/Label
@onready var _button_more :MenuButton = $ButtonMore
@onready var _airbrush_options: MenuButton = %AirbrushOptions
@onready var _pencil_options: MenuButton = %PencilOptions
@onready var _eraser_options: MenuButton = %EraserOptions
@onready var _tween_radius :Tween = null
@onready var _tween_density :Tween = null
func _ready() -> void:
edit_scale = _create_slider("", 0.01, 10.0, 0.01, DEFAULT_SCALE)
edit_rotation = _create_slider("", 0.0, 360.0, 0.1, DEFAULT_ROTATION)
edit_rotation_rand = _create_slider("", 0.0, 1.0, 0.01, DEFAULT_ROTATION_RAND)
edit_distance = _create_slider("", 0.0, 5.0, 0.01, DEFAULT_DISTANCE)
%ScaleCont.add_child(edit_scale)
%RotationCont.add_child(edit_rotation)
%RotationRandCont.add_child(edit_rotation_rand)
%DistanceCont.add_child(edit_distance)
func _unhandled_input(event :InputEvent) -> void:
if not event.is_pressed():
return
if _shortcut_radius_inc.matches_event(event):
slider_radius.value += 0.1
if _shortcut_radius_dec.matches_event(event):
slider_radius.value -= 0.1
if slider_density.editable and _shortcut_density_inc.matches_event(event):
slider_density.value += 1
if slider_density.editable and _shortcut_density_dec.matches_event(event):
slider_density.value -= 1
func set_plugin(plugin :EditorPlugin) -> void:
_plugin = plugin
theme_changed.connect(_on_theme_changed)
%ButtonMore.set_plugin(plugin)
var config := ConfigFile.new()
config.load("res://addons/simplegrasstextured/plugin.cfg")
%LabelVersion.text = config.get_value("plugin", "version")
button_airbrush.shortcut = plugin.get_custom_setting("SimpleGrassTextured/Shortcuts/airbrush_tool")
button_pencil.shortcut = plugin.get_custom_setting("SimpleGrassTextured/Shortcuts/pencil_tool")
button_eraser.shortcut = plugin.get_custom_setting("SimpleGrassTextured/Shortcuts/eraser_tool")
button_airbrush.gui_input.connect(_on_button_tool_gui_input.bind(button_airbrush, _airbrush_options))
button_pencil.gui_input.connect(_on_button_tool_gui_input.bind(button_pencil, _pencil_options))
button_eraser.gui_input.connect(_on_button_tool_gui_input.bind(button_eraser, _eraser_options))
_shortcut_radius_inc = plugin.get_custom_setting("SimpleGrassTextured/Shortcuts/radius_increment")
_shortcut_radius_dec = plugin.get_custom_setting("SimpleGrassTextured/Shortcuts/radius_decrement")
_shortcut_density_inc = plugin.get_custom_setting("SimpleGrassTextured/Shortcuts/density_increment")
_shortcut_density_dec = plugin.get_custom_setting("SimpleGrassTextured/Shortcuts/density_decrement")
_airbrush_options.get_popup().add_radio_check_item("Cylinder", MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER)
_airbrush_options.get_popup().add_radio_check_item("Box", MENU_SHAPE_ID.TOOL_SHAPE_BOX)
_airbrush_options.get_popup().about_to_popup.connect(_on_tool_options_about_to_popup.bind(_airbrush_options))
_airbrush_options.get_popup().id_pressed.connect(_on_sgt_shape_menu_pressed.bind("airbrush", _airbrush_options.get_popup()))
_pencil_options.get_popup().add_radio_check_item("Cylinder", MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER)
_pencil_options.get_popup().add_radio_check_item("Box", MENU_SHAPE_ID.TOOL_SHAPE_BOX)
_pencil_options.get_popup().about_to_popup.connect(_on_tool_options_about_to_popup.bind(_pencil_options))
_pencil_options.get_popup().id_pressed.connect(_on_sgt_shape_menu_pressed.bind("pencil", _pencil_options.get_popup()))
_eraser_options.get_popup().add_radio_check_item("Sphere", MENU_SHAPE_ID.TOOL_SHAPE_SPHERE)
_eraser_options.get_popup().add_radio_check_item("Cylinder", MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER)
_eraser_options.get_popup().add_radio_check_item("Infinite vertical cylinder", MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER_INF_H)
_eraser_options.get_popup().add_radio_check_item("Box", MENU_SHAPE_ID.TOOL_SHAPE_BOX)
_eraser_options.get_popup().add_radio_check_item("Infinite vertical box", MENU_SHAPE_ID.TOOL_SHAPE_BOX_INF_H)
_eraser_options.get_popup().about_to_popup.connect(_on_tool_options_about_to_popup.bind(_eraser_options))
_eraser_options.get_popup().id_pressed.connect(_on_sgt_shape_menu_pressed.bind("eraser", _eraser_options.get_popup()))
_on_theme_changed()
func set_current_grass(grass) -> void:
_grass_selected = grass
%ButtonMore.set_current_grass(grass)
if _grass_selected == null:
return
for tool_name in _grass_selected.sgt_tool_shape:
match tool_name:
"airbrush":
_update_shape_menu_from_grass(_airbrush_options.get_popup(), _grass_selected.sgt_tool_shape[tool_name])
"pencil":
_update_shape_menu_from_grass(_pencil_options.get_popup(), _grass_selected.sgt_tool_shape[tool_name])
"eraser":
_update_shape_menu_from_grass(_eraser_options.get_popup(), _grass_selected.sgt_tool_shape[tool_name])
func set_density_modulate(color: Color) -> void:
%IconDensity.self_modulate = color
slider_density.modulate = color
func _create_slider(label :String, min_value :float, max_value :float, step :float, value :float = 0.0) -> EditorSpinSlider:
var slider := EditorSpinSlider.new()
slider.size_flags_horizontal = Control.SIZE_EXPAND_FILL
slider.step = step;
slider.min_value = min_value
slider.max_value = max_value
slider.label = label
slider.value = value
slider.custom_minimum_size.x = 75
return slider
func _on_tool_options_about_to_popup(_button_options: MenuButton) -> void:
set_current_grass(_grass_selected)
func _update_shape_menu_from_grass(popupmenu: PopupMenu, plugin_id_shape: int) -> void:
for i in popupmenu.item_count:
popupmenu.set_item_checked(i, false)
var idx := -1
match _plugin.get_tool_shape_name(plugin_id_shape):
"sphere":
idx = popupmenu.get_item_index(MENU_SHAPE_ID.TOOL_SHAPE_SPHERE)
"cylinder":
idx = popupmenu.get_item_index(MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER)
"cylinder_inf_h":
idx = popupmenu.get_item_index(MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER_INF_H)
"box":
idx = popupmenu.get_item_index(MENU_SHAPE_ID.TOOL_SHAPE_BOX)
"box_inf_h":
idx = popupmenu.get_item_index(MENU_SHAPE_ID.TOOL_SHAPE_BOX_INF_H)
_:
idx = -1
if idx == -1:
return
popupmenu.set_item_checked(idx, true)
func _on_sgt_shape_menu_pressed(id: int, tool_name: String, popupmenu: PopupMenu) -> void:
if _grass_selected == null:
return
for i in popupmenu.item_count:
popupmenu.set_item_checked(i, false)
var idx := popupmenu.get_item_index(id)
if idx == -1:
return
popupmenu.set_item_checked(idx, true)
var shape_name := ""
match id:
MENU_SHAPE_ID.TOOL_SHAPE_SPHERE:
shape_name = "sphere"
MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER:
shape_name = "cylinder"
MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER_INF_H:
shape_name = "cylinder_inf_h"
MENU_SHAPE_ID.TOOL_SHAPE_BOX:
shape_name = "box"
MENU_SHAPE_ID.TOOL_SHAPE_BOX_INF_H:
shape_name = "box_inf_h"
_plugin.set_tool_shape(tool_name, shape_name)
_plugin.set_tool(tool_name)
func _on_h_slider_radius_value_changed(value :float) -> void:
if _tween_radius != null:
_tween_radius.kill()
_label_radius.set(&"theme_override_colors/font_outline_color", _label_radius.get_theme_color(&"font_color").inverted())
_tween_radius = _label_radius.create_tween()
_tween_radius.tween_property(_label_radius, ^"modulate", Color.WHITE, 0.1)
_tween_radius.tween_interval(2)
_tween_radius.tween_property(_label_radius, ^"modulate", Color(1,1,1,0), 2)
_label_radius.text = "%0.1f" % value
slider_radius.tooltip_text = "(" + _shortcut_radius_dec.get_as_text() + ") - (" + _shortcut_radius_inc.get_as_text() + ")\n"
slider_radius.tooltip_text += "Radius = %0.1f" % value
func _on_h_slider_density_value_changed(value :float) -> void:
if _tween_density != null:
_tween_density.kill()
_label_density.set(&"theme_override_colors/font_outline_color", _label_density.get_theme_color(&"font_color").inverted())
_tween_density = _label_density.create_tween()
_tween_density.tween_property(_label_density, ^"modulate", Color.WHITE, 0.1)
_tween_density.tween_interval(2)
_tween_density.tween_property(_label_density, ^"modulate", Color(1,1,1,0), 2)
_label_density.text = str(value)
slider_density.tooltip_text = "(" + _shortcut_density_dec.get_as_text() + ") - (" + _shortcut_density_inc.get_as_text() + ")\n"
slider_density.tooltip_text += "Density = %0.0f" % value
func _on_theme_changed() -> void:
%IconScale.icon = get_theme_icon(&"ToolScale", &"EditorIcons")
%IconRotation.icon = get_theme_icon(&"ToolRotate", &"EditorIcons")
%IconRotationRand.icon = get_theme_icon(&"RandomNumberGenerator", &"EditorIcons")
var base_color: Color = EditorInterface.get_base_control().get_theme_color(&"base_color", &"Editor")
if base_color.get_luminance() < 0.5:
button_airbrush.icon = load("res://addons/simplegrasstextured/images/sgt_icon_airbrush.svg")
button_pencil.icon = load("res://addons/simplegrasstextured/images/sgt_icon_pen.svg")
button_eraser.icon = load("res://addons/simplegrasstextured/images/sgt_icon_eraser.svg")
_airbrush_options.icon = load("res://addons/simplegrasstextured/images/sgt_icon_arrow_up.svg")
_pencil_options.icon = load("res://addons/simplegrasstextured/images/sgt_icon_arrow_up.svg")
_eraser_options.icon = load("res://addons/simplegrasstextured/images/sgt_icon_arrow_up.svg")
%IconSlope.icon = load("res://addons/simplegrasstextured/images/sgt_icon_slope.svg")
%IconRadius.icon = load("res://addons/simplegrasstextured/images/sgt_icon_radius.svg")
%IconDensity.icon = load("res://addons/simplegrasstextured/images/sgt_icon_density.svg")
%IconDistance.icon = load("res://addons/simplegrasstextured/images/sgt_icon_distance.svg")
else:
button_airbrush.icon = load("res://addons/simplegrasstextured/images/sgt_icon_airbrush_dark.svg")
button_pencil.icon = load("res://addons/simplegrasstextured/images/sgt_icon_pen_dark.svg")
button_eraser.icon = load("res://addons/simplegrasstextured/images/sgt_icon_eraser_dark.svg")
_airbrush_options.icon = load("res://addons/simplegrasstextured/images/sgt_icon_arrow_up_dark.svg")
_pencil_options.icon = load("res://addons/simplegrasstextured/images/sgt_icon_arrow_up_dark.svg")
_eraser_options.icon = load("res://addons/simplegrasstextured/images/sgt_icon_arrow_up_dark.svg")
%IconSlope.icon = load("res://addons/simplegrasstextured/images/sgt_icon_slope_dark.svg")
%IconRadius.icon = load("res://addons/simplegrasstextured/images/sgt_icon_radius_dark.svg")
%IconDensity.icon = load("res://addons/simplegrasstextured/images/sgt_icon_density_dark.svg")
%IconDistance.icon = load("res://addons/simplegrasstextured/images/sgt_icon_distance_dark.svg")
if _button_more != null:
_button_more.icon = get_theme_icon(&"GuiTabMenuHl", &"EditorIcons")
# Test that the icons size matches with editor UI scale
var _ed_scale := 1.0
var es: int = EditorInterface.get_editor_settings().get_setting("interface/editor/display_scale")
if es == 7:
_ed_scale = EditorInterface.get_editor_settings().get_setting("interface/editor/custom_display_scale")
else:
_ed_scale = [1.0, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0][clamp(es, 0, 6)]
if load("res://addons/simplegrasstextured/images/sgt_icon_density.svg").get_width() != roundi(16 * _ed_scale):
$TimerReimportIcons.start()
func _on_panel_container_gui_input(event) -> void:
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
var win = load("res://addons/simplegrasstextured/gui/about.tscn").instantiate()
get_window().add_child(win)
win.popup_centered()
func _on_icon_slope_pressed() -> void:
edit_slope_range.set_value_min(0)
edit_slope_range.set_value_max(45)
func _on_icon_scale_pressed() -> void:
edit_scale.value = DEFAULT_SCALE
func _on_icon_rotation_pressed() -> void:
edit_rotation.value = DEFAULT_ROTATION
func _on_icon_rotation_rand_pressed() -> void:
edit_rotation_rand.value = DEFAULT_ROTATION_RAND
func _on_icon_distance_pressed() -> void:
edit_distance.value = DEFAULT_DISTANCE
func _on_icon_radius_pressed() -> void:
slider_radius.value = DEFAULT_RADIUS
func _on_icon_radius_2_pressed() -> void:
slider_density.value = DEFAULT_DENSITY
func _on_timer_reimport_icons_timeout() -> void:
if EditorInterface.get_resource_filesystem().is_scanning():
return
$TimerReimportIcons.stop()
EditorInterface.get_resource_filesystem().reimport_files([
"res://addons/simplegrasstextured/sgt_icon.svg",
"res://addons/simplegrasstextured/sgt_icon_48.svg",
"res://addons/simplegrasstextured/images/sgt_icon_density.svg",
"res://addons/simplegrasstextured/images/sgt_icon_distance.svg",
"res://addons/simplegrasstextured/images/sgt_icon_radius.svg",
"res://addons/simplegrasstextured/images/sgt_icon_slope.svg",
"res://addons/simplegrasstextured/images/sgt_icon_airbrush.svg",
"res://addons/simplegrasstextured/images/sgt_icon_pen.svg",
"res://addons/simplegrasstextured/images/sgt_icon_eraser.svg"
])
func _on_button_tool_gui_input(event: InputEvent, tool: Button, button_options: MenuButton) -> void:
if event is InputEventMouseButton:
if event.pressed and event.button_index == MOUSE_BUTTON_RIGHT:
button_options.get_popup().popup(Rect2(tool.global_position + Vector2(tool.size.x, 0), Vector2.ZERO))