# 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))