Added footsteps, new tree, various other tweaks
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
@tool
|
||||
extends EditorProperty
|
||||
|
||||
|
||||
var _ui: Control
|
||||
|
||||
|
||||
func _init():
|
||||
_ui = preload("./ui/stack_panel.tscn").instantiate()
|
||||
add_child(_ui)
|
||||
set_bottom_editor(_ui)
|
||||
|
||||
|
||||
func set_node(object) -> void:
|
||||
_ui.set_node(object)
|
||||
@@ -0,0 +1,19 @@
|
||||
@tool
|
||||
extends EditorInspectorPlugin
|
||||
|
||||
|
||||
const Editor = preload("./editor_property.gd")
|
||||
const Scatter = preload("../../scatter.gd")
|
||||
|
||||
|
||||
func _can_handle(object):
|
||||
return is_instance_of(object, Scatter)
|
||||
|
||||
|
||||
func _parse_property(object, type, name, hint_type, hint_string, usage_flags, wide):
|
||||
if name == "modifier_stack":
|
||||
var editor_property = Editor.new()
|
||||
editor_property.set_node(object)
|
||||
add_property_editor("modifier_stack", editor_property)
|
||||
return true
|
||||
return false
|
||||
@@ -0,0 +1,19 @@
|
||||
@tool
|
||||
extends Button
|
||||
|
||||
|
||||
@onready var _popup: PopupPanel = $ModifiersPopup
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_popup.popup_hide.connect(_on_popup_closed)
|
||||
|
||||
|
||||
func _toggled(button_pressed):
|
||||
if button_pressed:
|
||||
_popup.position = global_position + Vector2(0.0, size.y)
|
||||
_popup.popup()
|
||||
|
||||
|
||||
func _on_popup_closed() -> void:
|
||||
button_pressed = false
|
||||
@@ -0,0 +1,59 @@
|
||||
@tool
|
||||
extends Control
|
||||
|
||||
|
||||
signal value_changed
|
||||
|
||||
|
||||
var _scatter
|
||||
var _previous
|
||||
var _locked := false
|
||||
|
||||
|
||||
func set_scatter(scatter_node) -> void:
|
||||
_scatter = scatter_node
|
||||
|
||||
|
||||
func set_parameter_name(_text: String) -> void:
|
||||
pass
|
||||
|
||||
|
||||
func set_hint_string(_hint: String) -> void:
|
||||
pass
|
||||
|
||||
|
||||
func set_value(val) -> void:
|
||||
_locked = true
|
||||
_set_value(val)
|
||||
_previous = get_value()
|
||||
_locked = false
|
||||
|
||||
|
||||
func get_value():
|
||||
pass
|
||||
|
||||
|
||||
func get_editor_theme() -> Theme:
|
||||
if not _scatter:
|
||||
return ThemeDB.get_default_theme()
|
||||
|
||||
var editor_interface: Variant
|
||||
|
||||
if Engine.get_version_info().minor >= 2:
|
||||
editor_interface = EditorInterface
|
||||
return editor_interface.get_editor_theme()
|
||||
else:
|
||||
editor_interface = _scatter.editor_plugin.get_editor_interface()
|
||||
return editor_interface.get_base_control().get_theme()
|
||||
|
||||
|
||||
func _set_value(_val):
|
||||
pass
|
||||
|
||||
|
||||
func _on_value_changed(_val) -> void:
|
||||
if not _locked:
|
||||
var value = get_value()
|
||||
if value != _previous:
|
||||
value_changed.emit(value, _previous)
|
||||
_previous = value
|
||||
@@ -0,0 +1,51 @@
|
||||
[gd_scene load_steps=4 format=3 uid="uid://cf4lrr5tnlwnw"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_lylt6"]
|
||||
content_margin_left = 0.0
|
||||
content_margin_top = 0.0
|
||||
content_margin_right = 0.0
|
||||
content_margin_bottom = 0.0
|
||||
bg_color = Color(1, 1, 1, 0.54902)
|
||||
corner_radius_top_left = 2
|
||||
corner_radius_top_right = 2
|
||||
corner_radius_bottom_right = 2
|
||||
corner_radius_bottom_left = 2
|
||||
corner_detail = 6
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_8hejw"]
|
||||
content_margin_left = 0.0
|
||||
content_margin_top = 0.0
|
||||
content_margin_right = 0.0
|
||||
content_margin_bottom = 0.0
|
||||
bg_color = Color(1, 1, 1, 0.784314)
|
||||
corner_radius_top_left = 2
|
||||
corner_radius_top_right = 2
|
||||
corner_radius_bottom_right = 2
|
||||
corner_radius_bottom_left = 2
|
||||
corner_detail = 6
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_dmtgy"]
|
||||
content_margin_left = 0.0
|
||||
content_margin_top = 0.0
|
||||
content_margin_right = 0.0
|
||||
content_margin_bottom = 0.0
|
||||
bg_color = Color(1, 1, 1, 1)
|
||||
corner_radius_top_left = 2
|
||||
corner_radius_top_right = 2
|
||||
corner_radius_bottom_right = 2
|
||||
corner_radius_bottom_left = 2
|
||||
corner_detail = 6
|
||||
|
||||
[node name="Button" type="Button"]
|
||||
custom_minimum_size = Vector2(20, 20)
|
||||
size_flags_horizontal = 3
|
||||
focus_mode = 0
|
||||
theme_override_colors/font_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_pressed_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_hover_color = Color(0, 0, 0, 1)
|
||||
theme_override_font_sizes/font_size = 12
|
||||
theme_override_styles/normal = SubResource("StyleBoxFlat_lylt6")
|
||||
theme_override_styles/hover = SubResource("StyleBoxFlat_8hejw")
|
||||
theme_override_styles/pressed = SubResource("StyleBoxFlat_dmtgy")
|
||||
toggle_mode = true
|
||||
text = "00"
|
||||
@@ -0,0 +1,347 @@
|
||||
# warning-ignore-all:return_value_discarded
|
||||
|
||||
@tool
|
||||
extends Control
|
||||
|
||||
|
||||
signal curve_updated
|
||||
|
||||
|
||||
@export var grid_color := Color(1, 1, 1, 0.2)
|
||||
@export var grid_color_sub := Color(1, 1, 1, 0.1)
|
||||
@export var curve_color := Color(1, 1, 1, 0.9)
|
||||
@export var point_color := Color.WHITE
|
||||
@export var selected_point_color := Color.ORANGE
|
||||
@export var point_radius := 4.0
|
||||
@export var text_color := Color(0.9, 0.9, 0.9)
|
||||
@export var columns := 4
|
||||
@export var rows := 2
|
||||
@export var dynamic_row_count := true
|
||||
|
||||
var curve: Curve
|
||||
var gt: Transform2D
|
||||
|
||||
var _hover_point := -1:
|
||||
set(val):
|
||||
if val != _hover_point:
|
||||
_hover_point = val
|
||||
queue_redraw()
|
||||
|
||||
var _selected_point := -1:
|
||||
set(val):
|
||||
if val != _selected_point:
|
||||
_selected_point = val
|
||||
queue_redraw()
|
||||
|
||||
var _selected_tangent := -1:
|
||||
set(val):
|
||||
if val != _selected_tangent:
|
||||
_selected_tangent = val
|
||||
queue_redraw()
|
||||
|
||||
var _dragging := false
|
||||
var _hover_radius := 50.0 # Squared
|
||||
var _tangents_length := 30.0
|
||||
var _font: Font
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
#rect_min_size.y *= EditorUtil.get_editor_scale()
|
||||
var plugin := EditorPlugin.new()
|
||||
var editor_theme := plugin.get_editor_interface().get_base_control().get_theme()
|
||||
if editor_theme:
|
||||
_font = editor_theme.get_font("Main", "EditorFonts")
|
||||
else:
|
||||
_font = ThemeDB.fallback_font
|
||||
plugin.queue_free()
|
||||
|
||||
queue_redraw()
|
||||
connect("resized", _on_resized)
|
||||
|
||||
|
||||
func set_curve(c: Curve) -> void:
|
||||
curve = c
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func get_curve() -> Curve:
|
||||
return curve
|
||||
|
||||
|
||||
func _gui_input(event) -> void:
|
||||
if event is InputEventKey:
|
||||
if _selected_point != -1 and event.scancode == KEY_DELETE:
|
||||
remove_point(_selected_point)
|
||||
|
||||
elif event is InputEventMouseButton:
|
||||
if event.double_click:
|
||||
add_point(_to_curve_space(event.position))
|
||||
|
||||
elif event.pressed and event.button_index == MOUSE_BUTTON_MIDDLE:
|
||||
var i = get_point_at(event.position)
|
||||
if i != -1:
|
||||
remove_point(i)
|
||||
|
||||
elif event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
|
||||
set_selected_tangent(get_tangent_at(event.position))
|
||||
|
||||
if _selected_tangent == -1:
|
||||
set_selected_point(get_point_at(event.position))
|
||||
if _selected_point != -1:
|
||||
_dragging = true
|
||||
|
||||
elif _dragging and not event.pressed:
|
||||
_dragging = false
|
||||
emit_signal("curve_updated")
|
||||
|
||||
elif event is InputEventMouseMotion:
|
||||
if _dragging:
|
||||
var curve_amplitude: float = curve.get_max_value() - curve.get_min_value()
|
||||
|
||||
# Snap to "round" coordinates when holding Ctrl.
|
||||
# Be more precise when holding Shift as well.
|
||||
var snap_threshold: float
|
||||
if event.ctrl_pressed:
|
||||
snap_threshold = 0.025 if event.shift else 0.1
|
||||
else:
|
||||
snap_threshold = 0.0
|
||||
|
||||
if _selected_tangent == -1: # Drag point
|
||||
var point_pos: Vector2 = _to_curve_space(event.position).snapped(Vector2(snap_threshold, snap_threshold * curve_amplitude))
|
||||
|
||||
# The index may change if the point is dragged across another one
|
||||
var i: int = curve.set_point_offset(_selected_point, point_pos.x)
|
||||
set_hover(i)
|
||||
set_selected_point(i)
|
||||
|
||||
# This is to prevent the user from losing a point out of view.
|
||||
if point_pos.y < curve.get_min_value():
|
||||
point_pos.y = curve.get_min_value()
|
||||
elif point_pos.y > curve.get_max_value():
|
||||
point_pos.y = curve.get_max_value()
|
||||
|
||||
curve.set_point_value(_selected_point, point_pos.y)
|
||||
|
||||
else: # Drag tangent
|
||||
var point_pos: Vector2 = curve.get_point_position(_selected_point)
|
||||
var control_pos: Vector2 = _to_curve_space(event.position).snapped(Vector2(snap_threshold, snap_threshold * curve_amplitude))
|
||||
|
||||
var dir: Vector2 = (control_pos - point_pos).normalized()
|
||||
|
||||
var tangent: float
|
||||
if not is_zero_approx(dir.x):
|
||||
tangent = dir.y / dir.x
|
||||
else:
|
||||
tangent = 1 if dir.y >= 0 else -1
|
||||
tangent *= 9999
|
||||
|
||||
var link: bool = not Input.is_key_pressed(KEY_SHIFT)
|
||||
|
||||
if _selected_tangent == 0:
|
||||
curve.set_point_left_tangent(_selected_point, tangent)
|
||||
|
||||
# Note: if a tangent is set to linear, it shouldn't be linked to the other
|
||||
if link and _selected_point != (curve.get_point_count() - 1) and curve.get_point_right_mode(_selected_point) != Curve.TANGENT_LINEAR:
|
||||
curve.set_point_right_tangent(_selected_point, tangent)
|
||||
|
||||
else:
|
||||
curve.set_point_right_tangent(_selected_point, tangent)
|
||||
|
||||
if link and _selected_point != 0 and curve.get_point_left_mode(_selected_point) != Curve.TANGENT_LINEAR:
|
||||
curve.set_point_left_tangent(_selected_point, tangent)
|
||||
queue_redraw()
|
||||
else:
|
||||
set_hover(get_point_at(event.position))
|
||||
|
||||
|
||||
func add_point(pos: Vector2) -> void:
|
||||
if not curve:
|
||||
return
|
||||
|
||||
pos.y = clamp(pos.y, 0.0, 1.0)
|
||||
curve.add_point(pos)
|
||||
queue_redraw()
|
||||
emit_signal("curve_updated")
|
||||
|
||||
|
||||
func remove_point(idx: int) -> void:
|
||||
if not curve:
|
||||
return
|
||||
|
||||
if idx == _selected_point:
|
||||
set_selected_point(-1)
|
||||
|
||||
if idx == _hover_point:
|
||||
set_hover(-1)
|
||||
|
||||
curve.remove_point(idx)
|
||||
queue_redraw()
|
||||
emit_signal("curve_updated")
|
||||
|
||||
|
||||
func get_point_at(pos: Vector2) -> int:
|
||||
if not curve:
|
||||
return -1
|
||||
|
||||
for i in curve.get_point_count():
|
||||
var p := _to_view_space(curve.get_point_position(i))
|
||||
if p.distance_squared_to(pos) <= _hover_radius:
|
||||
return i
|
||||
|
||||
return -1
|
||||
|
||||
|
||||
func get_tangent_at(pos: Vector2) -> int:
|
||||
if not curve or _selected_point < 0:
|
||||
return -1
|
||||
|
||||
if _selected_point != 0:
|
||||
var control_pos: Vector2 = _get_tangent_view_pos(_selected_point, 0)
|
||||
if control_pos.distance_squared_to(pos) < _hover_radius:
|
||||
return 0
|
||||
|
||||
if _selected_point != curve.get_point_count() - 1:
|
||||
var control_pos = _get_tangent_view_pos(_selected_point, 1)
|
||||
if control_pos.distance_squared_to(pos) < _hover_radius:
|
||||
return 1
|
||||
|
||||
return -1
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
if not curve:
|
||||
return
|
||||
|
||||
var text_height = _font.get_height()
|
||||
var min_outer := Vector2(0, size.y)
|
||||
var max_outer := Vector2(size.x, 0)
|
||||
var min_inner := Vector2(text_height, size.y - text_height)
|
||||
var max_inner := Vector2(size.x - text_height, text_height)
|
||||
|
||||
var width: float = max_inner.x - min_inner.x
|
||||
var height: float = max_inner.y - min_inner.y
|
||||
|
||||
var curve_min: float = curve.get_min_value()
|
||||
var curve_max: float = curve.get_max_value()
|
||||
|
||||
|
||||
# Main area
|
||||
draw_line(Vector2(0, max_inner.y), Vector2(max_outer.x, max_inner.y), grid_color)
|
||||
draw_line(Vector2(0, min_inner.y), Vector2(max_outer.x, min_inner.y), grid_color)
|
||||
draw_line(Vector2(min_inner.x, max_outer.y), Vector2(min_inner.x, min_outer.y), grid_color)
|
||||
draw_line(Vector2(max_inner.x, max_outer.y), Vector2(max_inner.x, min_outer.y), grid_color)
|
||||
|
||||
# Grid and scale
|
||||
## Vertical lines
|
||||
var x_offset = 1.0 / columns
|
||||
var margin = 4
|
||||
|
||||
for i in columns + 1:
|
||||
var x = width * (i * x_offset) + min_inner.x
|
||||
draw_line(Vector2(x, max_outer.y), Vector2(x, min_outer.y), grid_color_sub)
|
||||
draw_string(_font, Vector2(x + margin, min_outer.y - margin), str(snapped(i * x_offset, 0.01)), 0, -1, -1, text_color)
|
||||
|
||||
## Horizontal lines
|
||||
var y_offset = 1.0 / rows
|
||||
|
||||
for i in rows + 1:
|
||||
var y = height * (i * y_offset) + min_inner.y
|
||||
draw_line(Vector2(min_outer.x, y), Vector2(max_outer.x, y), grid_color_sub)
|
||||
var y_value = i * ((curve_max - curve_min) / rows) + curve_min
|
||||
draw_string(_font, Vector2(min_inner.x + margin, y - margin), str(snapped(y_value, 0.01)), 0, -1, -1, text_color)
|
||||
|
||||
# Plot curve
|
||||
var steps = 100
|
||||
var offset = 1.0 / steps
|
||||
x_offset = width / steps
|
||||
|
||||
var a: float
|
||||
var a_y: float
|
||||
var b: float
|
||||
var b_y: float
|
||||
|
||||
a = curve.sample_baked(0.0)
|
||||
a_y = remap(a, curve_min, curve_max, min_inner.y, max_inner.y)
|
||||
|
||||
for i in steps - 1:
|
||||
b = curve.sample_baked((i + 1) * offset)
|
||||
b_y = remap(b, curve_min, curve_max, min_inner.y, max_inner.y)
|
||||
draw_line(Vector2(min_inner.x + x_offset * i, a_y), Vector2(min_inner.x + x_offset * (i + 1), b_y), curve_color)
|
||||
a_y = b_y
|
||||
|
||||
# Draw points
|
||||
for i in curve.get_point_count():
|
||||
var pos: Vector2 = _to_view_space(curve.get_point_position(i))
|
||||
if _selected_point == i:
|
||||
draw_circle(pos, point_radius, selected_point_color)
|
||||
else:
|
||||
draw_circle(pos, point_radius, point_color);
|
||||
|
||||
if _hover_point == i:
|
||||
draw_arc(pos, point_radius + 4.0, 0.0, 2 * PI, 12, point_color, 1.0, true)
|
||||
|
||||
# Draw tangents
|
||||
if _selected_point >= 0:
|
||||
var i: int = _selected_point
|
||||
var pos: Vector2 = _to_view_space(curve.get_point_position(i))
|
||||
|
||||
if i != 0:
|
||||
var control_pos: Vector2 = _get_tangent_view_pos(i, 0)
|
||||
draw_line(pos, control_pos, selected_point_color)
|
||||
draw_rect(Rect2(control_pos, Vector2(1, 1)).grow(2), selected_point_color)
|
||||
|
||||
if i != curve.get_point_count() - 1:
|
||||
var control_pos: Vector2 = _get_tangent_view_pos(i, 1)
|
||||
draw_line(pos, control_pos, selected_point_color)
|
||||
draw_rect(Rect2(control_pos, Vector2(1, 1)).grow(2), selected_point_color)
|
||||
|
||||
|
||||
func _to_view_space(pos: Vector2) -> Vector2:
|
||||
var h = _font.get_height()
|
||||
pos.x = remap(pos.x, 0.0, 1.0, h, size.x - h)
|
||||
pos.y = remap(pos.y, curve.get_min_value(), curve.get_max_value(), size.y - h, h)
|
||||
return pos
|
||||
|
||||
|
||||
func _to_curve_space(pos: Vector2) -> Vector2:
|
||||
var h = _font.get_height()
|
||||
pos.x = remap(pos.x, h, size.x - h, 0.0, 1.0)
|
||||
pos.y = remap(pos.y, size.y - h, h, curve.get_min_value(), curve.get_max_value())
|
||||
return pos
|
||||
|
||||
|
||||
func _get_tangent_view_pos(i: int, tangent: int) -> Vector2:
|
||||
var dir: Vector2
|
||||
|
||||
if tangent == 0:
|
||||
dir = -Vector2(1.0, curve.get_point_left_tangent(i))
|
||||
else:
|
||||
dir = Vector2(1.0, curve.get_point_right_tangent(i))
|
||||
|
||||
var point_pos = _to_view_space(curve.get_point_position(i))
|
||||
var control_pos = _to_view_space(curve.get_point_position(i) + dir)
|
||||
|
||||
return point_pos + _tangents_length * (control_pos - point_pos).normalized()
|
||||
|
||||
|
||||
func set_hover(val: int) -> void:
|
||||
if val != _hover_point:
|
||||
_hover_point = val
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func set_selected_point(val: int) -> void:
|
||||
if val != _selected_point:
|
||||
_selected_point = val
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func set_selected_tangent(val: int) -> void:
|
||||
if val != _selected_tangent:
|
||||
_selected_tangent = val
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func _on_resized() -> void:
|
||||
if dynamic_row_count:
|
||||
rows = (int(size.y / custom_minimum_size.y) + 1) * 2
|
||||
@@ -0,0 +1,23 @@
|
||||
@tool
|
||||
extends "../base_parameter.gd"
|
||||
|
||||
|
||||
var _button
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_button = get_node("Button")
|
||||
_button.toggled.connect(_on_value_changed)
|
||||
|
||||
|
||||
func enable(enabled: bool) -> void:
|
||||
_button.disabled = not enabled
|
||||
_button.flat = not enabled
|
||||
|
||||
|
||||
func get_value() -> bool:
|
||||
return _button.button_pressed
|
||||
|
||||
|
||||
func _set_value(val: bool) -> void:
|
||||
_button.button_pressed = val
|
||||
@@ -0,0 +1,19 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://w6ycb4oveqhd"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/header/parameter_button.gd" id="1_f6puy"]
|
||||
[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/toggle_button.gd" id="2_167vc"]
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer"]
|
||||
offset_right = 40.0
|
||||
offset_bottom = 40.0
|
||||
size_flags_horizontal = 4
|
||||
size_flags_vertical = 4
|
||||
script = ExtResource( "1_f6puy" )
|
||||
|
||||
[node name="Button" type="Button" parent="."]
|
||||
offset_right = 40.0
|
||||
offset_bottom = 40.0
|
||||
focus_mode = 0
|
||||
toggle_mode = true
|
||||
icon_alignment = 1
|
||||
script = ExtResource( "2_167vc" )
|
||||
@@ -0,0 +1,17 @@
|
||||
@tool
|
||||
extends "../base_parameter.gd"
|
||||
|
||||
|
||||
@onready var _spinbox = $SpinBox
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_spinbox.value_changed.connect(_on_value_changed)
|
||||
|
||||
|
||||
func get_value() -> int:
|
||||
return int(_spinbox.get_value())
|
||||
|
||||
|
||||
func _set_value(val: int) -> void:
|
||||
_spinbox.set_value(val)
|
||||
@@ -0,0 +1,17 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://c36gqn03pvlnr"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/header/parameter_spinbox.gd" id="1_f0oq6"]
|
||||
|
||||
[node name="ParameterSpinbox" type="MarginContainer"]
|
||||
offset_right = 83.0625
|
||||
offset_bottom = 31.0
|
||||
size_flags_horizontal = 4
|
||||
size_flags_vertical = 4
|
||||
script = ExtResource( "1_f0oq6" )
|
||||
|
||||
[node name="SpinBox" type="SpinBox" parent="."]
|
||||
offset_right = 83.0
|
||||
offset_bottom = 31.0
|
||||
min_value = -100.0
|
||||
allow_greater = true
|
||||
allow_lesser = true
|
||||
@@ -0,0 +1,131 @@
|
||||
@tool
|
||||
extends "base_parameter.gd"
|
||||
|
||||
|
||||
@onready var _label: Label = $Label
|
||||
@onready var _grid_1: Control = $%GridContainer1
|
||||
@onready var _grid_2: Control = $%GridContainer2
|
||||
@onready var _grid_3: Control = $%GridContainer3
|
||||
@onready var _grid_4: Control = $%GridContainer4
|
||||
@onready var _menu_button: MenuButton = $%MenuButton
|
||||
|
||||
var _buttons: Array[Button]
|
||||
var _popup: PopupMenu
|
||||
var _layer_count := 32
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_buttons = []
|
||||
var grids = [_grid_1, _grid_2, _grid_3, _grid_4]
|
||||
|
||||
for g in grids:
|
||||
for c in g.get_children():
|
||||
if c is Button:
|
||||
var layer_number = c.text.to_int()
|
||||
if layer_number > _layer_count:
|
||||
c.visible = false
|
||||
continue
|
||||
_buttons.push_front(c)
|
||||
c.focus_mode = Control.FOCUS_NONE
|
||||
c.pressed.connect(_on_button_pressed)
|
||||
|
||||
_popup = _menu_button.get_popup()
|
||||
_popup.clear()
|
||||
|
||||
var layer_name := ""
|
||||
for i in _layer_count:
|
||||
if i != 0 and i % 4 == 0:
|
||||
_popup.add_separator("", 100 + i)
|
||||
|
||||
layer_name = ProjectSettings.get_setting("layer_names/3d_physics/layer_" + str(i + 1))
|
||||
if layer_name.is_empty():
|
||||
layer_name = "Layer " + str(i + 1)
|
||||
_popup.add_check_item(layer_name, _layer_count - 1 - i)
|
||||
|
||||
_sync_popup_state()
|
||||
_popup.id_pressed.connect(_on_id_pressed)
|
||||
|
||||
|
||||
func set_parameter_name(text: String) -> void:
|
||||
_label.text = text
|
||||
|
||||
|
||||
func _set_value(val: int) -> void:
|
||||
var binary_string: String = _dec2bin(val)
|
||||
var length = binary_string.length()
|
||||
|
||||
if length < _layer_count:
|
||||
binary_string = binary_string.pad_zeros(_layer_count)
|
||||
elif length > _layer_count:
|
||||
binary_string = binary_string.substr(length - _layer_count, length)
|
||||
|
||||
for i in _layer_count:
|
||||
_buttons[i].button_pressed = binary_string[i] == "1"
|
||||
|
||||
_sync_popup_state()
|
||||
|
||||
|
||||
func get_value() -> int:
|
||||
var binary_string = ""
|
||||
for b in _buttons:
|
||||
binary_string += "1" if b.button_pressed else "0"
|
||||
|
||||
var val = _bin2dec(binary_string)
|
||||
return val
|
||||
|
||||
|
||||
func _dec2bin(value: int) -> String:
|
||||
if value == 0:
|
||||
return "0"
|
||||
|
||||
var binary_string = ""
|
||||
while value != 0:
|
||||
var m = value % 2
|
||||
binary_string = str(m) + binary_string
|
||||
# warning-ignore:integer_division
|
||||
value = value / 2
|
||||
|
||||
return binary_string
|
||||
|
||||
|
||||
func _bin2dec(binary_string: String) -> int:
|
||||
var decimal_value = 0
|
||||
var count = binary_string.length() - 1
|
||||
|
||||
for i in binary_string.length():
|
||||
decimal_value += pow(2, count) * binary_string[i].to_int()
|
||||
count -= 1
|
||||
|
||||
return decimal_value
|
||||
|
||||
|
||||
func _sync_popup_state() -> void:
|
||||
if not _popup:
|
||||
return
|
||||
|
||||
for i in _layer_count:
|
||||
var idx = _popup.get_item_index(i)
|
||||
_popup.set_item_checked(idx, _buttons[i].button_pressed)
|
||||
|
||||
|
||||
func _on_button_pressed() -> void:
|
||||
_on_value_changed(null)
|
||||
_sync_popup_state()
|
||||
|
||||
|
||||
func _on_id_pressed(id: int) -> void:
|
||||
var idx = _popup.get_item_index(id)
|
||||
var checked = not _popup.is_item_checked(idx)
|
||||
_buttons[id].button_pressed = checked
|
||||
_popup.set_item_checked(idx, checked)
|
||||
_on_button_pressed()
|
||||
|
||||
|
||||
func _on_enable_all_pressed() -> void:
|
||||
_set_value(4294967295)
|
||||
_on_value_changed(null)
|
||||
|
||||
|
||||
func _on_clear_pressed() -> void:
|
||||
_set_value(0)
|
||||
_on_value_changed(null)
|
||||
@@ -0,0 +1,335 @@
|
||||
[gd_scene load_steps=6 format=3 uid="uid://chondv2lhs4pl"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/parameter_bitmask.gd" id="1"]
|
||||
[ext_resource type="PackedScene" uid="uid://cf4lrr5tnlwnw" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/bitmask_button.tscn" id="2"]
|
||||
[ext_resource type="Texture2D" uid="uid://n66mufjib4ds" path="res://addons/proton_scatter/icons/menu.svg" id="3"]
|
||||
[ext_resource type="Texture2D" uid="uid://bosx22dy64f11" path="res://addons/proton_scatter/icons/clear.svg" id="4"]
|
||||
[ext_resource type="Texture2D" uid="uid://uytbptu3a34s" path="res://addons/proton_scatter/icons/select_all.svg" id="4_h30jm"]
|
||||
|
||||
[node name="parameter_bitmask" type="VBoxContainer"]
|
||||
anchors_preset = 10
|
||||
anchor_right = 1.0
|
||||
offset_bottom = 178.0
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="Label" type="Label" parent="."]
|
||||
layout_mode = 2
|
||||
text = "Parameter name"
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer"]
|
||||
layout_mode = 2
|
||||
alignment = 2
|
||||
|
||||
[node name="MenuButton" type="MenuButton" parent="MarginContainer/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
icon = ExtResource("3")
|
||||
item_count = 39
|
||||
popup/item_0/text = "Layer 1"
|
||||
popup/item_0/checkable = 1
|
||||
popup/item_0/id = 31
|
||||
popup/item_1/text = "Layer 2"
|
||||
popup/item_1/checkable = 1
|
||||
popup/item_1/id = 30
|
||||
popup/item_2/text = "Layer 3"
|
||||
popup/item_2/checkable = 1
|
||||
popup/item_2/id = 29
|
||||
popup/item_3/text = "Layer 4"
|
||||
popup/item_3/checkable = 1
|
||||
popup/item_3/id = 28
|
||||
popup/item_4/text = ""
|
||||
popup/item_4/id = 104
|
||||
popup/item_4/separator = true
|
||||
popup/item_5/text = "Layer 5"
|
||||
popup/item_5/checkable = 1
|
||||
popup/item_5/id = 27
|
||||
popup/item_6/text = "Layer 6"
|
||||
popup/item_6/checkable = 1
|
||||
popup/item_6/id = 26
|
||||
popup/item_7/text = "Layer 7"
|
||||
popup/item_7/checkable = 1
|
||||
popup/item_7/id = 25
|
||||
popup/item_8/text = "Layer 8"
|
||||
popup/item_8/checkable = 1
|
||||
popup/item_8/id = 24
|
||||
popup/item_9/text = ""
|
||||
popup/item_9/id = 108
|
||||
popup/item_9/separator = true
|
||||
popup/item_10/text = "Layer 9"
|
||||
popup/item_10/checkable = 1
|
||||
popup/item_10/id = 23
|
||||
popup/item_11/text = "Layer 10"
|
||||
popup/item_11/checkable = 1
|
||||
popup/item_11/id = 22
|
||||
popup/item_12/text = "Layer 11"
|
||||
popup/item_12/checkable = 1
|
||||
popup/item_12/id = 21
|
||||
popup/item_13/text = "Layer 12"
|
||||
popup/item_13/checkable = 1
|
||||
popup/item_13/id = 20
|
||||
popup/item_14/text = ""
|
||||
popup/item_14/id = 112
|
||||
popup/item_14/separator = true
|
||||
popup/item_15/text = "Layer 13"
|
||||
popup/item_15/checkable = 1
|
||||
popup/item_15/id = 19
|
||||
popup/item_16/text = "Layer 14"
|
||||
popup/item_16/checkable = 1
|
||||
popup/item_16/id = 18
|
||||
popup/item_17/text = "Layer 15"
|
||||
popup/item_17/checkable = 1
|
||||
popup/item_17/id = 17
|
||||
popup/item_18/text = "Layer 16"
|
||||
popup/item_18/checkable = 1
|
||||
popup/item_18/id = 16
|
||||
popup/item_19/text = ""
|
||||
popup/item_19/id = 116
|
||||
popup/item_19/separator = true
|
||||
popup/item_20/text = "Layer 17"
|
||||
popup/item_20/checkable = 1
|
||||
popup/item_20/id = 15
|
||||
popup/item_21/text = "Layer 18"
|
||||
popup/item_21/checkable = 1
|
||||
popup/item_21/id = 14
|
||||
popup/item_22/text = "Layer 19"
|
||||
popup/item_22/checkable = 1
|
||||
popup/item_22/id = 13
|
||||
popup/item_23/text = "Layer 20"
|
||||
popup/item_23/checkable = 1
|
||||
popup/item_23/id = 12
|
||||
popup/item_24/text = ""
|
||||
popup/item_24/id = 120
|
||||
popup/item_24/separator = true
|
||||
popup/item_25/text = "Layer 21"
|
||||
popup/item_25/checkable = 1
|
||||
popup/item_25/id = 11
|
||||
popup/item_26/text = "Layer 22"
|
||||
popup/item_26/checkable = 1
|
||||
popup/item_26/id = 10
|
||||
popup/item_27/text = "Layer 23"
|
||||
popup/item_27/checkable = 1
|
||||
popup/item_27/id = 9
|
||||
popup/item_28/text = "Layer 24"
|
||||
popup/item_28/checkable = 1
|
||||
popup/item_28/id = 8
|
||||
popup/item_29/text = ""
|
||||
popup/item_29/id = 124
|
||||
popup/item_29/separator = true
|
||||
popup/item_30/text = "Layer 25"
|
||||
popup/item_30/checkable = 1
|
||||
popup/item_30/id = 7
|
||||
popup/item_31/text = "Layer 26"
|
||||
popup/item_31/checkable = 1
|
||||
popup/item_31/id = 6
|
||||
popup/item_32/text = "Layer 27"
|
||||
popup/item_32/checkable = 1
|
||||
popup/item_32/id = 5
|
||||
popup/item_33/text = "Layer 28"
|
||||
popup/item_33/checkable = 1
|
||||
popup/item_33/id = 4
|
||||
popup/item_34/text = ""
|
||||
popup/item_34/id = 128
|
||||
popup/item_34/separator = true
|
||||
popup/item_35/text = "Layer 29"
|
||||
popup/item_35/checkable = 1
|
||||
popup/item_35/id = 3
|
||||
popup/item_36/text = "Layer 30"
|
||||
popup/item_36/checkable = 1
|
||||
popup/item_36/id = 2
|
||||
popup/item_37/text = "Layer 31"
|
||||
popup/item_37/checkable = 1
|
||||
popup/item_37/id = 1
|
||||
popup/item_38/text = "Layer 32"
|
||||
popup/item_38/checkable = 1
|
||||
popup/item_38/id = 0
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/HBoxContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="GridContainer1" type="GridContainer" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
columns = 4
|
||||
|
||||
[node name="Button1" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer1" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "1"
|
||||
|
||||
[node name="Button2" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer1" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "2"
|
||||
|
||||
[node name="Button3" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer1" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "3"
|
||||
|
||||
[node name="Button4" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer1" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "4"
|
||||
|
||||
[node name="Button5" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer1" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "5"
|
||||
|
||||
[node name="Button6" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer1" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "6"
|
||||
|
||||
[node name="Button7" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer1" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "7"
|
||||
|
||||
[node name="Button8" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer1" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "8"
|
||||
|
||||
[node name="VSeparator" type="VSeparator" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="GridContainer2" type="GridContainer" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
columns = 4
|
||||
|
||||
[node name="Button9" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer2" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "9"
|
||||
|
||||
[node name="Button10" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer2" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "10"
|
||||
|
||||
[node name="Button11" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer2" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "11"
|
||||
|
||||
[node name="Button12" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer2" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "12"
|
||||
|
||||
[node name="Button13" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer2" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "13"
|
||||
|
||||
[node name="Button14" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer2" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "14"
|
||||
|
||||
[node name="Button15" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer2" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "15"
|
||||
|
||||
[node name="Button16" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer2" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "16"
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="MarginContainer/HBoxContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HBoxContainer2" type="HBoxContainer" parent="MarginContainer/HBoxContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="GridContainer3" type="GridContainer" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
columns = 4
|
||||
|
||||
[node name="Button17" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer3" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "17"
|
||||
|
||||
[node name="Button18" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer3" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "18"
|
||||
|
||||
[node name="Button19" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer3" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "19"
|
||||
|
||||
[node name="Button20" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer3" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "20"
|
||||
|
||||
[node name="Button21" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer3" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "21"
|
||||
|
||||
[node name="Button22" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer3" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "22"
|
||||
|
||||
[node name="Button23" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer3" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "23"
|
||||
|
||||
[node name="Button24" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer3" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "24"
|
||||
|
||||
[node name="VSeparator2" type="VSeparator" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="GridContainer4" type="GridContainer" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
columns = 4
|
||||
|
||||
[node name="Button25" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer4" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "25"
|
||||
|
||||
[node name="Button26" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer4" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "26"
|
||||
|
||||
[node name="Button27" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer4" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "27"
|
||||
|
||||
[node name="Button28" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer4" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "28"
|
||||
|
||||
[node name="Button29" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer4" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "29"
|
||||
|
||||
[node name="Button30" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer4" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "30"
|
||||
|
||||
[node name="Button31" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer4" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "31"
|
||||
|
||||
[node name="Button32" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer4" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
text = "32"
|
||||
|
||||
[node name="VBoxContainer2" type="VBoxContainer" parent="MarginContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="EnableAll" type="Button" parent="MarginContainer/HBoxContainer/VBoxContainer2"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
focus_mode = 0
|
||||
icon = ExtResource("4_h30jm")
|
||||
flat = true
|
||||
expand_icon = true
|
||||
|
||||
[node name="ClearButton" type="Button" parent="MarginContainer/HBoxContainer/VBoxContainer2"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
focus_mode = 0
|
||||
icon = ExtResource("4")
|
||||
flat = true
|
||||
|
||||
[connection signal="pressed" from="MarginContainer/HBoxContainer/VBoxContainer2/EnableAll" to="." method="_on_enable_all_pressed"]
|
||||
[connection signal="pressed" from="MarginContainer/HBoxContainer/VBoxContainer2/ClearButton" to="." method="_on_clear_pressed"]
|
||||
@@ -0,0 +1,23 @@
|
||||
@tool
|
||||
extends "base_parameter.gd"
|
||||
|
||||
|
||||
@onready var _label: Label = $Label
|
||||
@onready var _check_box: CheckBox = $CheckBox
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
# warning-ignore:return_value_discarded
|
||||
_check_box.connect("toggled", _on_value_changed)
|
||||
|
||||
|
||||
func set_parameter_name(text: String) -> void:
|
||||
_label.text = text
|
||||
|
||||
|
||||
func get_value() -> bool:
|
||||
return _check_box.button_pressed
|
||||
|
||||
|
||||
func _set_value(val: bool) -> void:
|
||||
_check_box.button_pressed = val
|
||||
@@ -0,0 +1,21 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://10wqs13p5i3d"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/parameter_bool.gd" id="1"]
|
||||
|
||||
[node name="ParameterScalar" type="HBoxContainer"]
|
||||
anchor_right = 1.0
|
||||
script = ExtResource( "1" )
|
||||
|
||||
[node name="Label" type="Label" parent="."]
|
||||
offset_top = 2.0
|
||||
offset_right = 996.0
|
||||
offset_bottom = 28.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Parameter name"
|
||||
|
||||
[node name="CheckBox" type="CheckBox" parent="."]
|
||||
offset_left = 1000.0
|
||||
offset_right = 1024.0
|
||||
offset_bottom = 31.0
|
||||
focus_mode = 0
|
||||
mouse_filter = 1
|
||||
@@ -0,0 +1,25 @@
|
||||
@tool
|
||||
extends "base_parameter.gd"
|
||||
|
||||
|
||||
const Util = preload("../../../../../common/util.gd")
|
||||
|
||||
|
||||
@onready var _label: Label = $Label
|
||||
@onready var _panel: Control = $MarginContainer/CurvePanel
|
||||
|
||||
|
||||
func set_parameter_name(text: String) -> void:
|
||||
_label.text = text
|
||||
|
||||
|
||||
func get_value() -> Curve:
|
||||
return _panel.get_curve()
|
||||
|
||||
|
||||
func _set_value(val: Curve) -> void:
|
||||
_panel.set_curve(val)
|
||||
|
||||
|
||||
func _on_curve_updated() -> void:
|
||||
_on_value_changed(get_value())
|
||||
@@ -0,0 +1,26 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://dqjwibwhdmgsb"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/parameter_curve.gd" id="1"]
|
||||
[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/curve_panel.gd" id="2"]
|
||||
|
||||
[node name="ParameterCurve" type="VBoxContainer"]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="Label" type="Label" parent="."]
|
||||
layout_mode = 2
|
||||
text = "Curve name"
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="CurvePanel" type="PanelContainer" parent="MarginContainer"]
|
||||
custom_minimum_size = Vector2(0, 100)
|
||||
layout_mode = 2
|
||||
script = ExtResource("2")
|
||||
selected_point_color = Color(0.878431, 0.47451, 0, 1)
|
||||
rows = 4
|
||||
|
||||
[connection signal="curve_updated" from="MarginContainer/CurvePanel" to="." method="_on_curve_updated"]
|
||||
@@ -0,0 +1,54 @@
|
||||
@tool
|
||||
extends "base_parameter.gd"
|
||||
|
||||
|
||||
@onready var _label: Label = $%Label
|
||||
@onready var _select_button: Button = $%FileButton
|
||||
@onready var _dialog: FileDialog = $%FileDialog
|
||||
@onready var _texture: Button = $%TextureButton
|
||||
@onready var _preview_root: Control = $%PreviewRoot
|
||||
|
||||
var _path := ""
|
||||
var _is_texture := false
|
||||
|
||||
|
||||
func set_parameter_name(text: String) -> void:
|
||||
_label.text = text
|
||||
|
||||
|
||||
func set_hint_string(hint: String) -> void:
|
||||
_is_texture = hint == "Texture"
|
||||
_set_value(get_value())
|
||||
|
||||
|
||||
func _set_value(val: String) -> void:
|
||||
_path = val
|
||||
_select_button.text = val.get_file()
|
||||
_preview_root.visible = false
|
||||
|
||||
if val.is_empty():
|
||||
_select_button.text = "Select a file"
|
||||
|
||||
if _is_texture:
|
||||
var texture = load(get_value())
|
||||
if texture is Texture:
|
||||
_texture.icon = texture
|
||||
_preview_root.visible = true
|
||||
|
||||
|
||||
func get_value() -> String:
|
||||
return _path
|
||||
|
||||
|
||||
func _on_clear_button_pressed() -> void:
|
||||
_set_value("")
|
||||
_on_value_changed("")
|
||||
|
||||
|
||||
func _on_select_button_pressed() -> void:
|
||||
_dialog.popup_centered()
|
||||
|
||||
|
||||
func _on_file_selected(file: String) -> void:
|
||||
_set_value(file)
|
||||
_on_value_changed(file)
|
||||
@@ -0,0 +1,67 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://cvgj4rdc0mxxq"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://bosx22dy64f11" path="res://addons/proton_scatter/icons/clear.svg" id="1"]
|
||||
[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/parameter_file.gd" id="2"]
|
||||
|
||||
[node name="ParameterFile" type="VBoxContainer"]
|
||||
anchors_preset = 10
|
||||
anchor_right = 1.0
|
||||
offset_bottom = 31.0
|
||||
size_flags_vertical = 0
|
||||
theme_override_constants/separation = 0
|
||||
script = ExtResource("2")
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="."]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Parameter name"
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="HBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="FileButton" type="Button" parent="HBoxContainer/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Select a file"
|
||||
|
||||
[node name="ClearButton" type="Button" parent="HBoxContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
icon = ExtResource("1")
|
||||
|
||||
[node name="PreviewRoot" type="HBoxContainer" parent="."]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="Control" type="Control" parent="PreviewRoot"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="TextureButton" type="Button" parent="PreviewRoot"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(100, 100)
|
||||
layout_mode = 2
|
||||
flat = true
|
||||
expand_icon = true
|
||||
|
||||
[node name="Control" type="Control" parent="."]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="FileDialog" type="FileDialog" parent="Control"]
|
||||
unique_name_in_owner = true
|
||||
title = "Open a File"
|
||||
size = Vector2i(400, 600)
|
||||
ok_button_text = "Open"
|
||||
file_mode = 0
|
||||
filters = PackedStringArray("*.bmp", "*.dds", "*.exr", "*.hdr", "*.jpg", "*.jpeg", "*.png", "*.tga", "*.svg", "*.svgz", "*.webp")
|
||||
|
||||
[connection signal="pressed" from="HBoxContainer/HBoxContainer/FileButton" to="." method="_on_select_button_pressed"]
|
||||
[connection signal="pressed" from="HBoxContainer/HBoxContainer/ClearButton" to="." method="_on_clear_button_pressed"]
|
||||
[connection signal="pressed" from="PreviewRoot/TextureButton" to="." method="_on_select_button_pressed"]
|
||||
[connection signal="file_selected" from="Control/FileDialog" to="." method="_on_file_selected"]
|
||||
@@ -0,0 +1,88 @@
|
||||
@tool
|
||||
extends "base_parameter.gd"
|
||||
|
||||
|
||||
@onready var _label: Label = $%Label
|
||||
@onready var _select_button: Button = $%SelectButton
|
||||
@onready var _popup: ConfirmationDialog = $%ConfirmationDialog
|
||||
@onready var _tree: Tree = $%Tree
|
||||
|
||||
var _full_path: NodePath
|
||||
var _root: Node
|
||||
var _selected: Node
|
||||
|
||||
|
||||
func set_root(root) -> void:
|
||||
_root = root
|
||||
|
||||
|
||||
func set_parameter_name(text: String) -> void:
|
||||
_label.text = text
|
||||
|
||||
|
||||
func _set_value(val) -> void:
|
||||
if val == null:
|
||||
return
|
||||
|
||||
_full_path = val
|
||||
|
||||
if val.is_empty():
|
||||
return
|
||||
|
||||
_select_button.text = val.get_name(val.get_name_count() - 1)
|
||||
|
||||
if _root and _root.has_node(val):
|
||||
_selected = _root.get_node(val)
|
||||
|
||||
if val.is_empty():
|
||||
_select_button.text = "Select a node"
|
||||
|
||||
|
||||
func get_value() -> NodePath:
|
||||
#if _root and _selected:
|
||||
# _full_path = String(_root.get_path_to(_selected))
|
||||
return _full_path
|
||||
|
||||
|
||||
func _populate_tree() -> void:
|
||||
_tree.clear()
|
||||
var scene_root: Node = get_tree().get_edited_scene_root()
|
||||
var editor_theme: Theme = get_editor_theme()
|
||||
_create_items_recursive(scene_root, null, editor_theme)
|
||||
|
||||
|
||||
func _create_items_recursive(node: Node, parent: TreeItem, editor_theme: Theme) -> void:
|
||||
if parent and not node.owner:
|
||||
return # Hidden node.
|
||||
|
||||
var node_item = _tree.create_item(parent)
|
||||
node_item.set_text(0, node.get_name())
|
||||
node_item.set_meta("node", node)
|
||||
|
||||
var node_icon: Texture2D
|
||||
var node_class := node.get_class()
|
||||
if is_instance_valid(editor_theme):
|
||||
if editor_theme.has_icon(node_class, "EditorIcons"):
|
||||
node_icon = editor_theme.get_icon(node_class, "EditorIcons")
|
||||
else:
|
||||
node_icon = editor_theme.get_icon("Node", "EditorIcons")
|
||||
node_item.set_icon(0, node_icon)
|
||||
|
||||
for child in node.get_children():
|
||||
_create_items_recursive(child, node_item, editor_theme)
|
||||
|
||||
|
||||
func _on_select_button_pressed() -> void:
|
||||
_populate_tree()
|
||||
_popup.popup_centered(Vector2i(400, 600))
|
||||
|
||||
|
||||
func _on_clear_button_pressed() -> void:
|
||||
_select_button.text = "Select a node"
|
||||
_full_path = NodePath()
|
||||
|
||||
|
||||
func _on_node_selected():
|
||||
var node = _tree.get_selected().get_meta("node")
|
||||
_set_value(_root.get_path_to(node))
|
||||
_on_value_changed(get_value())
|
||||
@@ -0,0 +1,66 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://bku7i3ct7ftui"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://bosx22dy64f11" path="res://addons/proton_scatter/icons/clear.svg" id="1"]
|
||||
[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/parameter_node_selector.gd" id="2"]
|
||||
|
||||
[node name="NodeSelector" type="MarginContainer"]
|
||||
anchors_preset = 10
|
||||
anchor_right = 1.0
|
||||
script = ExtResource("2")
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="."]
|
||||
layout_mode = 2
|
||||
offset_right = 1152.0
|
||||
offset_bottom = 31.0
|
||||
|
||||
[node name="Label" type="Label" parent="HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
offset_top = 2.0
|
||||
offset_right = 560.0
|
||||
offset_bottom = 28.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Parameter name"
|
||||
|
||||
[node name="SelectButton" type="Button" parent="HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
offset_left = 564.0
|
||||
offset_right = 1124.0
|
||||
offset_bottom = 31.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Select Node"
|
||||
flat = true
|
||||
|
||||
[node name="ClearButton" type="Button" parent="HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
offset_left = 1128.0
|
||||
offset_right = 1152.0
|
||||
offset_bottom = 31.0
|
||||
icon = ExtResource("1")
|
||||
|
||||
[node name="ConfirmationDialog" type="ConfirmationDialog" parent="."]
|
||||
unique_name_in_owner = true
|
||||
size = Vector2i(400, 500)
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="ConfirmationDialog"]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 8.0
|
||||
offset_top = 8.0
|
||||
offset_right = -960.0
|
||||
offset_bottom = -597.0
|
||||
|
||||
[node name="Tree" type="Tree" parent="ConfirmationDialog/ScrollContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
offset_right = 184.0
|
||||
offset_bottom = 43.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[connection signal="pressed" from="HBoxContainer/SelectButton" to="." method="_on_select_button_pressed"]
|
||||
[connection signal="pressed" from="HBoxContainer/ClearButton" to="." method="_on_clear_button_pressed"]
|
||||
[connection signal="confirmed" from="ConfirmationDialog" to="." method="_on_node_selected"]
|
||||
@@ -0,0 +1,119 @@
|
||||
# warning-ignore-all:return_value_discarded
|
||||
|
||||
@tool
|
||||
extends "base_parameter.gd"
|
||||
|
||||
|
||||
var _is_int := false
|
||||
var _is_enum := false
|
||||
|
||||
@onready var _label: Label = $Label
|
||||
@onready var _spinbox: SpinBox = $%SpinBox
|
||||
@onready var _option: OptionButton = $%OptionButton
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_spinbox.value_changed.connect(_on_value_changed)
|
||||
_option.item_selected.connect(_on_value_changed)
|
||||
mark_as_int(_is_int)
|
||||
|
||||
|
||||
func mark_as_int(val: bool) -> void:
|
||||
_is_int = val
|
||||
if _is_int and _spinbox:
|
||||
_spinbox.step = 1
|
||||
|
||||
|
||||
func mark_as_enum(val: bool) -> void:
|
||||
_is_enum = val
|
||||
|
||||
|
||||
func toggle_option_item(idx: int, value := false) -> void:
|
||||
_option.set_item_disabled(idx, not value)
|
||||
|
||||
|
||||
func set_parameter_name(text: String) -> void:
|
||||
_label.text = text
|
||||
|
||||
|
||||
func set_hint_string(hint: String) -> void:
|
||||
# No hint provided, ignore.
|
||||
if hint.is_empty():
|
||||
return
|
||||
|
||||
if hint == "float":
|
||||
_spinbox.step = 0.01
|
||||
return
|
||||
|
||||
if hint == "int":
|
||||
_spinbox.step = 1
|
||||
return
|
||||
|
||||
# One integer provided
|
||||
if hint.is_valid_int():
|
||||
_set_range(0, hint.to_int())
|
||||
return
|
||||
|
||||
# Multiple items provided, check their types
|
||||
var tokens = hint.split(",")
|
||||
var all_int = true
|
||||
var all_float = true
|
||||
|
||||
for t in tokens:
|
||||
if not t.is_valid_int():
|
||||
all_int = false
|
||||
if not t.is_valid_float():
|
||||
all_float = false
|
||||
|
||||
# All items are integer
|
||||
if all_int and tokens.size() >= 2:
|
||||
_set_range(tokens[0].to_int(), tokens[1].to_int())
|
||||
return
|
||||
|
||||
# All items are float
|
||||
if all_float:
|
||||
if tokens.size() >= 2:
|
||||
_set_range(tokens[0].to_float(), tokens[1].to_float())
|
||||
if tokens.size() >= 3:
|
||||
_spinbox.step = tokens[2].to_float()
|
||||
return
|
||||
|
||||
# All items are strings, make it a dropdown
|
||||
_spinbox.visible = false
|
||||
_option.visible = true
|
||||
_is_enum = true
|
||||
_is_int = true
|
||||
|
||||
for i in tokens.size():
|
||||
_option.add_item(_sanitize_option_name(tokens[i]), i)
|
||||
|
||||
set_value(int(_spinbox.get_value()))
|
||||
|
||||
|
||||
func get_value():
|
||||
if _is_enum:
|
||||
return _option.get_selected_id()
|
||||
if _is_int:
|
||||
return int(_spinbox.get_value())
|
||||
return _spinbox.get_value()
|
||||
|
||||
|
||||
func _set_value(val) -> void:
|
||||
if _is_int:
|
||||
val = int(val)
|
||||
if _is_enum:
|
||||
_option.select(val)
|
||||
else:
|
||||
_spinbox.set_value(val)
|
||||
|
||||
|
||||
func _set_range(start, end) -> void:
|
||||
if start < end:
|
||||
_spinbox.min_value = start
|
||||
_spinbox.max_value = end
|
||||
_spinbox.allow_greater = false
|
||||
_spinbox.allow_lesser = false
|
||||
|
||||
|
||||
func _sanitize_option_name(token: String) -> String:
|
||||
return token.left(token.find(":"))
|
||||
@@ -0,0 +1,55 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://bspbhkrpgak0e"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/parameter_scalar.gd" id="1"]
|
||||
|
||||
[node name="ParameterScalar" type="HBoxContainer"]
|
||||
anchors_preset = 10
|
||||
anchor_right = 1.0
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="Label" type="Label" parent="."]
|
||||
layout_mode = 2
|
||||
offset_top = 2.0
|
||||
offset_right = 1833.0
|
||||
offset_bottom = 28.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Parameter name"
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
layout_mode = 2
|
||||
offset_left = 1837.0
|
||||
offset_right = 1920.0
|
||||
offset_bottom = 31.0
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="Panel" type="Panel" parent="MarginContainer"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
offset_right = 83.0
|
||||
offset_bottom = 31.0
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="MarginContainer"]
|
||||
layout_mode = 2
|
||||
offset_right = 83.0
|
||||
offset_bottom = 31.0
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="SpinBox" type="SpinBox" parent="MarginContainer/MarginContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
offset_right = 83.0
|
||||
offset_bottom = 31.0
|
||||
mouse_filter = 1
|
||||
min_value = -100.0
|
||||
step = 0.001
|
||||
allow_greater = true
|
||||
allow_lesser = true
|
||||
|
||||
[node name="OptionButton" type="OptionButton" parent="MarginContainer/MarginContainer"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
offset_right = 83.0
|
||||
offset_bottom = 31.0
|
||||
focus_mode = 0
|
||||
@@ -0,0 +1,27 @@
|
||||
@tool
|
||||
extends "base_parameter.gd"
|
||||
|
||||
|
||||
@onready var _label: Label = $Label
|
||||
@onready var _line_edit: LineEdit = $MarginContainer/MarginContainer/LineEdit
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_line_edit.connect("text_entered", _on_value_changed)
|
||||
_line_edit.connect("focus_exited", _on_focus_exited)
|
||||
|
||||
|
||||
func set_parameter_name(text: String) -> void:
|
||||
_label.text = text
|
||||
|
||||
|
||||
func _set_value(val: String) -> void:
|
||||
_line_edit.text = val
|
||||
|
||||
|
||||
func get_value() -> String:
|
||||
return _line_edit.get_text()
|
||||
|
||||
|
||||
func _on_focus_exited() -> void:
|
||||
_on_value_changed(get_value())
|
||||
@@ -0,0 +1,56 @@
|
||||
[gd_scene load_steps=4 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/parameter_string.gd" id="1"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id=1]
|
||||
bg_color = Color( 0, 0, 0, 0.392157 )
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id=2]
|
||||
bg_color = Color( 0.6, 0.6, 0.6, 0 )
|
||||
|
||||
[node name="ParameterString" type="HBoxContainer"]
|
||||
anchor_right = 1.0
|
||||
script = ExtResource( 1 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Label" type="Label" parent="."]
|
||||
margin_top = 2.0
|
||||
margin_right = 638.0
|
||||
margin_bottom = 16.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Parameter name"
|
||||
valign = 1
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
margin_left = 642.0
|
||||
margin_right = 1280.0
|
||||
margin_bottom = 18.0
|
||||
mouse_filter = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="Panel" type="Panel" parent="MarginContainer"]
|
||||
margin_right = 638.0
|
||||
margin_bottom = 18.0
|
||||
mouse_filter = 2
|
||||
custom_styles/panel = SubResource( 1 )
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="MarginContainer"]
|
||||
margin_right = 638.0
|
||||
margin_bottom = 18.0
|
||||
mouse_filter = 2
|
||||
custom_constants/margin_right = 4
|
||||
custom_constants/margin_top = 2
|
||||
custom_constants/margin_left = 4
|
||||
custom_constants/margin_bottom = 2
|
||||
|
||||
[node name="LineEdit" type="LineEdit" parent="MarginContainer/MarginContainer"]
|
||||
margin_left = 4.0
|
||||
margin_top = 2.0
|
||||
margin_right = 634.0
|
||||
margin_bottom = 16.0
|
||||
mouse_filter = 1
|
||||
custom_styles/focus = SubResource( 2 )
|
||||
custom_styles/normal = SubResource( 2 )
|
||||
clear_button_enabled = true
|
||||
@@ -0,0 +1,47 @@
|
||||
# warning-ignore-all:return_value_discarded
|
||||
|
||||
@tool
|
||||
extends "base_parameter.gd"
|
||||
|
||||
|
||||
@onready var _label: Label = $Label
|
||||
@onready var _x: SpinBox = $%X
|
||||
@onready var _y: SpinBox = $%Y
|
||||
@onready var _link: Button = $%LinkButton
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_x.value_changed.connect(_on_spinbox_value_changed)
|
||||
_y.value_changed.connect(_on_spinbox_value_changed)
|
||||
|
||||
|
||||
func set_parameter_name(text: String) -> void:
|
||||
_label.text = text
|
||||
|
||||
|
||||
func get_value() -> Vector2:
|
||||
var vec2 = Vector2.ZERO
|
||||
vec2.x = _x.get_value()
|
||||
vec2.y = _y.get_value()
|
||||
return vec2
|
||||
|
||||
|
||||
func _set_value(val: Vector2) -> void:
|
||||
_x.set_value(val.x)
|
||||
_y.set_value(val.y)
|
||||
|
||||
|
||||
func _on_clear_pressed():
|
||||
var old = get_value()
|
||||
set_value(Vector2.ZERO)
|
||||
_previous = old
|
||||
_on_value_changed(Vector2.ZERO)
|
||||
|
||||
|
||||
func _on_spinbox_value_changed(value: float) -> void:
|
||||
if _link.button_pressed:
|
||||
var old = get_value()
|
||||
set_value(Vector2(value, value))
|
||||
_previous = old
|
||||
|
||||
_on_value_changed(get_value())
|
||||
@@ -0,0 +1,108 @@
|
||||
[gd_scene load_steps=4 format=3 uid="uid://bjn8ydwp80y7q"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/parameter_vector2.gd" id="1"]
|
||||
[ext_resource type="Texture2D" uid="uid://bosx22dy64f11" path="res://addons/proton_scatter/icons/clear.svg" id="2"]
|
||||
[ext_resource type="Texture2D" uid="uid://gbrmse47gdxb" path="res://addons/proton_scatter/icons/link.svg" id="3_u2lry"]
|
||||
|
||||
[node name="ParameterVector2" type="HBoxContainer"]
|
||||
anchors_preset = 10
|
||||
anchor_right = 1.0
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="Label" type="Label" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 5
|
||||
text = "Parameter name"
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="Panel" type="Panel" parent="MarginContainer"]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="MarginContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
size_flags_vertical = 4
|
||||
mouse_filter = 2
|
||||
theme_override_constants/margin_left = 6
|
||||
theme_override_constants/margin_right = 6
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/MarginContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="GridContainer" type="GridContainer" parent="MarginContainer/MarginContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/MarginContainer/HBoxContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="MarginContainer/MarginContainer/HBoxContainer/GridContainer/HBoxContainer"]
|
||||
modulate = Color(1, 0.447059, 0.368627, 1)
|
||||
layout_mode = 2
|
||||
text = "x"
|
||||
|
||||
[node name="X" type="SpinBox" parent="MarginContainer/MarginContainer/HBoxContainer/GridContainer/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
mouse_filter = 1
|
||||
min_value = -100.0
|
||||
step = 0.001
|
||||
allow_greater = true
|
||||
allow_lesser = true
|
||||
|
||||
[node name="HBoxContainer2" type="HBoxContainer" parent="MarginContainer/MarginContainer/HBoxContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="MarginContainer/MarginContainer/HBoxContainer/GridContainer/HBoxContainer2"]
|
||||
modulate = Color(0.564706, 0.992157, 0.298039, 1)
|
||||
layout_mode = 2
|
||||
text = "y"
|
||||
|
||||
[node name="Y" type="SpinBox" parent="MarginContainer/MarginContainer/HBoxContainer/GridContainer/HBoxContainer2"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
mouse_filter = 1
|
||||
min_value = -100.0
|
||||
step = 0.001
|
||||
allow_greater = true
|
||||
allow_lesser = true
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/MarginContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Control" type="Control" parent="MarginContainer/MarginContainer/HBoxContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="ClearButton" type="Button" parent="MarginContainer/MarginContainer/HBoxContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
focus_mode = 0
|
||||
mouse_filter = 1
|
||||
icon = ExtResource("2")
|
||||
flat = true
|
||||
|
||||
[node name="Control2" type="Control" parent="MarginContainer/MarginContainer/HBoxContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="LinkButton" type="Button" parent="MarginContainer/MarginContainer/HBoxContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
focus_mode = 0
|
||||
mouse_filter = 1
|
||||
toggle_mode = true
|
||||
icon = ExtResource("3_u2lry")
|
||||
flat = true
|
||||
|
||||
[node name="Control3" type="Control" parent="MarginContainer/MarginContainer/HBoxContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[connection signal="pressed" from="MarginContainer/MarginContainer/HBoxContainer/VBoxContainer/ClearButton" to="." method="_on_clear_pressed"]
|
||||
@@ -0,0 +1,49 @@
|
||||
@tool
|
||||
extends "base_parameter.gd"
|
||||
|
||||
|
||||
@onready var _label: Label = $Label
|
||||
@onready var _x: SpinBox = $%X
|
||||
@onready var _y: SpinBox = $%Y
|
||||
@onready var _z: SpinBox = $%Z
|
||||
@onready var _link: Button = $%LinkButton
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_x.value_changed.connect(_on_spinbox_value_changed)
|
||||
_y.value_changed.connect(_on_spinbox_value_changed)
|
||||
_z.value_changed.connect(_on_spinbox_value_changed)
|
||||
|
||||
|
||||
func set_parameter_name(text: String) -> void:
|
||||
_label.text = text
|
||||
|
||||
|
||||
func get_value() -> Vector3:
|
||||
var vec3 = Vector3.ZERO
|
||||
vec3.x = _x.get_value()
|
||||
vec3.y = _y.get_value()
|
||||
vec3.z = _z.get_value()
|
||||
return vec3
|
||||
|
||||
|
||||
func _set_value(val: Vector3) -> void:
|
||||
_x.set_value(val.x)
|
||||
_y.set_value(val.y)
|
||||
_z.set_value(val.z)
|
||||
|
||||
|
||||
func _on_clear_pressed():
|
||||
var old = get_value()
|
||||
set_value(Vector3.ZERO)
|
||||
_previous = old
|
||||
_on_value_changed(Vector3.ZERO)
|
||||
|
||||
|
||||
func _on_spinbox_value_changed(value: float) -> void:
|
||||
if _link.button_pressed:
|
||||
var old = get_value()
|
||||
set_value(Vector3(value, value, value))
|
||||
_previous = old
|
||||
|
||||
_on_value_changed(get_value())
|
||||
@@ -0,0 +1,127 @@
|
||||
[gd_scene load_steps=4 format=3 uid="uid://cdpfgf0447ph4"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/parameter_vector3.gd" id="1"]
|
||||
[ext_resource type="Texture2D" uid="uid://bosx22dy64f11" path="res://addons/proton_scatter/icons/clear.svg" id="2"]
|
||||
[ext_resource type="Texture2D" uid="uid://gbrmse47gdxb" path="res://addons/proton_scatter/icons/link.svg" id="3_gq2ti"]
|
||||
|
||||
[node name="ParameterVector3" type="HBoxContainer"]
|
||||
anchors_preset = 10
|
||||
anchor_right = 1.0
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="Label" type="Label" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 5
|
||||
text = "Parameter name"
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="Panel" type="Panel" parent="MarginContainer"]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="MarginContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
size_flags_vertical = 4
|
||||
mouse_filter = 2
|
||||
theme_override_constants/margin_left = 6
|
||||
theme_override_constants/margin_right = 6
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/MarginContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="GridContainer" type="GridContainer" parent="MarginContainer/MarginContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/MarginContainer/HBoxContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="MarginContainer/MarginContainer/HBoxContainer/GridContainer/HBoxContainer"]
|
||||
modulate = Color(1, 0.447059, 0.368627, 1)
|
||||
layout_mode = 2
|
||||
text = "x"
|
||||
|
||||
[node name="X" type="SpinBox" parent="MarginContainer/MarginContainer/HBoxContainer/GridContainer/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
mouse_filter = 1
|
||||
min_value = -100.0
|
||||
step = 0.001
|
||||
allow_greater = true
|
||||
allow_lesser = true
|
||||
|
||||
[node name="HBoxContainer2" type="HBoxContainer" parent="MarginContainer/MarginContainer/HBoxContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="MarginContainer/MarginContainer/HBoxContainer/GridContainer/HBoxContainer2"]
|
||||
modulate = Color(0.564706, 0.992157, 0.298039, 1)
|
||||
layout_mode = 2
|
||||
text = "y"
|
||||
|
||||
[node name="Y" type="SpinBox" parent="MarginContainer/MarginContainer/HBoxContainer/GridContainer/HBoxContainer2"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
mouse_filter = 1
|
||||
min_value = -100.0
|
||||
step = 0.001
|
||||
allow_greater = true
|
||||
allow_lesser = true
|
||||
|
||||
[node name="HBoxContainer3" type="HBoxContainer" parent="MarginContainer/MarginContainer/HBoxContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="MarginContainer/MarginContainer/HBoxContainer/GridContainer/HBoxContainer3"]
|
||||
modulate = Color(0.14902, 0.8, 1, 1)
|
||||
layout_mode = 2
|
||||
text = "z"
|
||||
|
||||
[node name="Z" type="SpinBox" parent="MarginContainer/MarginContainer/HBoxContainer/GridContainer/HBoxContainer3"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
mouse_filter = 1
|
||||
min_value = -100.0
|
||||
step = 0.001
|
||||
allow_greater = true
|
||||
allow_lesser = true
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/MarginContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
alignment = 1
|
||||
|
||||
[node name="Control3" type="Control" parent="MarginContainer/MarginContainer/HBoxContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="ClearButton" type="Button" parent="MarginContainer/MarginContainer/HBoxContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
focus_mode = 0
|
||||
mouse_filter = 1
|
||||
icon = ExtResource("2")
|
||||
flat = true
|
||||
|
||||
[node name="Control" type="Control" parent="MarginContainer/MarginContainer/HBoxContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="LinkButton" type="Button" parent="MarginContainer/MarginContainer/HBoxContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
focus_mode = 0
|
||||
mouse_filter = 1
|
||||
toggle_mode = true
|
||||
icon = ExtResource("3_gq2ti")
|
||||
flat = true
|
||||
|
||||
[node name="Control2" type="Control" parent="MarginContainer/MarginContainer/HBoxContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[connection signal="pressed" from="MarginContainer/MarginContainer/HBoxContainer/VBoxContainer/ClearButton" to="." method="_on_clear_pressed"]
|
||||
@@ -0,0 +1,96 @@
|
||||
@tool
|
||||
extends Container
|
||||
|
||||
# DragContainer
|
||||
# Custom containner similar to a VBoxContainer, but the user can rearrange the
|
||||
# children order via drag and drop. This is only used in the inspector plugin
|
||||
# for the modifier stack and won't work with arbitrary control nodes.
|
||||
|
||||
|
||||
signal child_moved(last_index: int, new_index: int)
|
||||
|
||||
|
||||
var _separation: int = 0
|
||||
var _drag_offset = null
|
||||
var _dragged_child = null
|
||||
var _old_index: int
|
||||
var _new_index: int
|
||||
var _map := [] # Stores the y top position of each child in the stack
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_separation = get_theme_constant("separation", "VBoxContainer")
|
||||
|
||||
|
||||
func _notification(what):
|
||||
if what == NOTIFICATION_SORT_CHILDREN or what == NOTIFICATION_RESIZED:
|
||||
_update_layout()
|
||||
|
||||
|
||||
func _can_drop_data(at_position, data) -> bool:
|
||||
if data.get_parent() != self:
|
||||
return false
|
||||
|
||||
# Drag just started
|
||||
if not _dragged_child:
|
||||
_dragged_child = data
|
||||
_drag_offset = at_position - data.position
|
||||
_old_index = data.get_index()
|
||||
_new_index = _old_index
|
||||
|
||||
# Dragged control only follow the y mouse position
|
||||
data.position.y = at_position.y - _drag_offset.y
|
||||
|
||||
# Check if the children order should be changed
|
||||
var computed_index = 0
|
||||
for pos_y in _map:
|
||||
if pos_y > data.position.y - 16:
|
||||
break
|
||||
computed_index += 1
|
||||
|
||||
# Prevents edge case when dragging the last item below its current position
|
||||
computed_index = clamp(computed_index, 0, get_child_count() - 1)
|
||||
|
||||
if computed_index != data.get_index():
|
||||
move_child(data, computed_index)
|
||||
_new_index = computed_index
|
||||
|
||||
return true
|
||||
|
||||
|
||||
# Called once at the end of the drag
|
||||
func _drop_data(at_position, data) -> void:
|
||||
_drag_offset = null
|
||||
_dragged_child = null
|
||||
_update_layout()
|
||||
|
||||
if _old_index != _new_index:
|
||||
child_moved.emit(_old_index, _new_index)
|
||||
|
||||
|
||||
# Detects if the user drops the children outside the container and treats it
|
||||
# as if the drop happened the moment the mouse left the container.
|
||||
func _unhandled_input(event):
|
||||
if not _dragged_child:
|
||||
return
|
||||
|
||||
if event is InputEventMouseButton and not event.pressed:
|
||||
_drop_data(_dragged_child.position, _dragged_child)
|
||||
|
||||
|
||||
func _update_layout() -> void:
|
||||
_map.clear()
|
||||
var offset := Vector2.ZERO
|
||||
|
||||
for c in get_children():
|
||||
if c is Control:
|
||||
_map.push_back(offset.y)
|
||||
var child_min_size = c.get_combined_minimum_size()
|
||||
var possible_space = Rect2(offset, Vector2(size.x, child_min_size.y))
|
||||
|
||||
if c != _dragged_child:
|
||||
fit_child_in_rect(c, possible_space)
|
||||
|
||||
offset.y += c.size.y + _separation
|
||||
|
||||
custom_minimum_size.y = offset.y - _separation
|
||||
@@ -0,0 +1,203 @@
|
||||
@tool
|
||||
extends Control
|
||||
|
||||
|
||||
signal value_changed
|
||||
signal removed
|
||||
signal documentation_requested
|
||||
signal duplication_requested
|
||||
|
||||
|
||||
const ParameterBool := preload("./components/parameter_bool.tscn")
|
||||
const ParameterScalar := preload("./components/parameter_scalar.tscn")
|
||||
const ParameterNodeSelector = preload("./components/parameter_node_selector.tscn")
|
||||
const ParameterFile = preload("./components/parameter_file.tscn")
|
||||
const ParameterCurve = preload("./components/parameter_curve.tscn")
|
||||
const ParameterBitmask = preload("./components/parameter_bitmask.tscn")
|
||||
const ParameterString = preload("./components/parameter_string.tscn")
|
||||
const ParameterVector3 = preload("./components/parameter_vector3.tscn")
|
||||
const ParameterVector2 = preload("./components/parameter_vector2.tscn")
|
||||
const PARAMETER_IGNORE_LIST := [
|
||||
"enabled",
|
||||
"override_global_seed",
|
||||
"custom_seed",
|
||||
"restrict_height",
|
||||
"reference_frame",
|
||||
]
|
||||
|
||||
var _scatter
|
||||
var _modifier
|
||||
|
||||
@onready var _parameters: Control = $%ParametersRoot
|
||||
@onready var _name: Label = $%ModifierName
|
||||
@onready var _expand: Button = $%Expand
|
||||
@onready var _enabled: Button = $%Enabled
|
||||
@onready var _remove: Button = $%Remove
|
||||
@onready var _warning: Button = $%Warning
|
||||
@onready var _warning_dialog: AcceptDialog = $WarningDialog
|
||||
@onready var _drag_control: Control = $%DragControl
|
||||
@onready var _override_ui = $%OverrideGlobalSeed
|
||||
@onready var _custom_seed_ui = $%CustomSeed
|
||||
@onready var _restrict_height_ui = $%RestrictHeight
|
||||
@onready var _transform_space_ui = $%TransformSpace
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_name.text = name
|
||||
_enabled.toggled.connect(_on_enable_toggled)
|
||||
_remove.pressed.connect(_on_remove_pressed)
|
||||
_warning.pressed.connect(_on_warning_icon_pressed)
|
||||
_expand.toggled.connect(_on_expand_toggled)
|
||||
$%MenuButton.get_popup().id_pressed.connect(_on_menu_item_pressed)
|
||||
|
||||
|
||||
func _get_drag_data(at_position: Vector2):
|
||||
var drag_control_position = _drag_control.global_position - global_position
|
||||
var drag_rect := Rect2(drag_control_position, _drag_control.size)
|
||||
if drag_rect.has_point(at_position):
|
||||
return self
|
||||
|
||||
return null
|
||||
|
||||
|
||||
func set_root(val) -> void:
|
||||
_scatter = val
|
||||
|
||||
|
||||
# Loops through all exposed parameters and create an UI component for each of
|
||||
# them. For special properties (listed in PARAMATER_IGNORE_LIST), a special
|
||||
# UI is created.
|
||||
func create_ui_for(modifier) -> void:
|
||||
_modifier = modifier
|
||||
_modifier.warning_changed.connect(_on_warning_changed)
|
||||
_on_warning_changed()
|
||||
|
||||
_name.text = modifier.display_name
|
||||
_enabled.button_pressed = modifier.enabled
|
||||
|
||||
# Enable or disable irrelevant controls for this modifier
|
||||
_override_ui.enable(modifier.can_override_seed)
|
||||
_restrict_height_ui.enable(modifier.can_restrict_height)
|
||||
_transform_space_ui.mark_as_enum(true)
|
||||
_transform_space_ui.toggle_option_item(0, modifier.global_reference_frame_available)
|
||||
_transform_space_ui.toggle_option_item(1, modifier.local_reference_frame_available)
|
||||
_transform_space_ui.toggle_option_item(2, modifier.individual_instances_reference_frame_available)
|
||||
if not modifier.global_reference_frame_available and \
|
||||
not modifier.local_reference_frame_available and \
|
||||
not modifier.individual_instances_reference_frame_available:
|
||||
_transform_space_ui.visible = false
|
||||
|
||||
# Setup header connections
|
||||
_override_ui.value_changed.connect(_on_parameter_value_changed.bind("override_global_seed", _override_ui))
|
||||
_custom_seed_ui.value_changed.connect(_on_parameter_value_changed.bind("custom_seed", _custom_seed_ui))
|
||||
_restrict_height_ui.value_changed.connect(_on_parameter_value_changed.bind("restrict_height", _restrict_height_ui))
|
||||
_transform_space_ui.value_changed.connect(_on_parameter_value_changed.bind("reference_frame", _transform_space_ui))
|
||||
|
||||
# Restore header values
|
||||
_override_ui.set_value(modifier.override_global_seed)
|
||||
_custom_seed_ui.set_value(modifier.custom_seed)
|
||||
_restrict_height_ui.set_value(modifier.restrict_height)
|
||||
_transform_space_ui.set_value(modifier.reference_frame)
|
||||
|
||||
# Loop over the other properties and create a ui component for each of them
|
||||
for property in modifier.get_property_list():
|
||||
if property.usage != PROPERTY_USAGE_DEFAULT + PROPERTY_USAGE_SCRIPT_VARIABLE:
|
||||
continue
|
||||
|
||||
if property.name in PARAMETER_IGNORE_LIST:
|
||||
continue
|
||||
|
||||
var parameter_ui
|
||||
match property.type:
|
||||
TYPE_BOOL:
|
||||
parameter_ui = ParameterBool.instantiate()
|
||||
TYPE_FLOAT:
|
||||
parameter_ui = ParameterScalar.instantiate()
|
||||
TYPE_INT:
|
||||
if property.hint == PROPERTY_HINT_LAYERS_3D_PHYSICS:
|
||||
parameter_ui = ParameterBitmask.instantiate()
|
||||
else:
|
||||
parameter_ui = ParameterScalar.instantiate()
|
||||
parameter_ui.mark_as_int(true)
|
||||
TYPE_STRING:
|
||||
if property.hint_string == "File" or property.hint_string == "Texture":
|
||||
parameter_ui = ParameterFile.instantiate()
|
||||
else:
|
||||
parameter_ui = ParameterString.instantiate()
|
||||
TYPE_VECTOR3:
|
||||
parameter_ui = ParameterVector3.instantiate()
|
||||
TYPE_VECTOR2:
|
||||
parameter_ui = ParameterVector2.instantiate()
|
||||
TYPE_NODE_PATH:
|
||||
parameter_ui = ParameterNodeSelector.instantiate()
|
||||
parameter_ui.set_root(_scatter)
|
||||
TYPE_OBJECT:
|
||||
if property.class_name == &"Curve":
|
||||
parameter_ui = ParameterCurve.instantiate()
|
||||
|
||||
if parameter_ui:
|
||||
_parameters.add_child(parameter_ui)
|
||||
parameter_ui.set_parameter_name(property.name.capitalize())
|
||||
parameter_ui.set_value(modifier.get(property.name))
|
||||
parameter_ui.set_hint_string(property.hint_string)
|
||||
parameter_ui.set_scatter(_scatter)
|
||||
parameter_ui.value_changed.connect(_on_parameter_value_changed.bind(property.name, parameter_ui))
|
||||
|
||||
_expand.button_pressed = _modifier.expanded
|
||||
|
||||
|
||||
func _restore_value(name, val, ui) -> void:
|
||||
_modifier.set(name, val)
|
||||
ui.set_value(val)
|
||||
value_changed.emit()
|
||||
|
||||
|
||||
func _on_expand_toggled(toggled: bool) -> void:
|
||||
$%ParametersContainer.visible = toggled
|
||||
_modifier.expanded = toggled
|
||||
|
||||
|
||||
func _on_remove_pressed() -> void:
|
||||
removed.emit()
|
||||
|
||||
|
||||
func _on_parameter_value_changed(value, previous, parameter_name, ui) -> void:
|
||||
if _scatter.undo_redo:
|
||||
_scatter.undo_redo.create_action("Change value " + parameter_name.capitalize())
|
||||
_scatter.undo_redo.add_undo_method(self, "_restore_value", parameter_name, previous, ui)
|
||||
_scatter.undo_redo.add_do_method(self, "_restore_value", parameter_name, value, ui)
|
||||
_scatter.undo_redo.commit_action()
|
||||
else:
|
||||
_modifier.set(parameter_name, value)
|
||||
value_changed.emit()
|
||||
|
||||
|
||||
func _on_enable_toggled(pressed: bool):
|
||||
_modifier.enabled = pressed
|
||||
value_changed.emit()
|
||||
|
||||
|
||||
func _on_removed_pressed() -> void:
|
||||
removed.emit()
|
||||
|
||||
|
||||
func _on_warning_changed() -> void:
|
||||
var warning = _modifier.get_warning()
|
||||
_warning.visible = (warning != "")
|
||||
_warning_dialog.dialog_text = warning
|
||||
|
||||
|
||||
func _on_warning_icon_pressed() -> void:
|
||||
_warning_dialog.popup_centered()
|
||||
|
||||
|
||||
func _on_menu_item_pressed(id) -> void:
|
||||
match id:
|
||||
0:
|
||||
documentation_requested.emit()
|
||||
2:
|
||||
duplication_requested.emit()
|
||||
3:
|
||||
_on_remove_pressed()
|
||||
_:
|
||||
pass
|
||||
@@ -0,0 +1,255 @@
|
||||
[gd_scene load_steps=21 format=3 uid="uid://blpobpd0eweog"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/modifier_panel.gd" id="1"]
|
||||
[ext_resource type="Texture2D" uid="uid://cu2t8yylseggu" path="res://addons/proton_scatter/icons/arrow_right.svg" id="2_2djuo"]
|
||||
[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/toggle_button.gd" id="4"]
|
||||
[ext_resource type="Texture2D" uid="uid://t8c6kjbvst0s" path="res://addons/proton_scatter/icons/arrow_down.svg" id="4_7nlfc"]
|
||||
[ext_resource type="Texture2D" uid="uid://dahwdjl2er75o" path="res://addons/proton_scatter/icons/close.svg" id="5"]
|
||||
[ext_resource type="Texture2D" uid="uid://n66mufjib4ds" path="res://addons/proton_scatter/icons/menu.svg" id="6_lmo8k"]
|
||||
[ext_resource type="Texture2D" uid="uid://d2ajwyebaobjt" path="res://addons/proton_scatter/icons/duplicate.svg" id="7_f6nan"]
|
||||
[ext_resource type="Texture2D" uid="uid://do8d3urxirjoa" path="res://addons/proton_scatter/icons/doc.svg" id="7_owhij"]
|
||||
[ext_resource type="Texture2D" uid="uid://dj0y6peid681t" path="res://addons/proton_scatter/icons/warning.svg" id="9"]
|
||||
[ext_resource type="Texture2D" uid="uid://ba6cx70dyeuhg" path="res://addons/proton_scatter/icons/drag_area.svg" id="9_t6pse"]
|
||||
[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/override_seed_button.gd" id="10_ptukr"]
|
||||
[ext_resource type="Texture2D" uid="uid://dmmefjvrdhf78" path="res://addons/proton_scatter/icons/dice.svg" id="11_qwhro"]
|
||||
[ext_resource type="PackedScene" uid="uid://w6ycb4oveqhd" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/header/parameter_button.tscn" id="11_y7srw"]
|
||||
[ext_resource type="Texture2D" uid="uid://cmvfdl1wnrw4" path="res://addons/proton_scatter/icons/restrict_volume.svg" id="12_lx60d"]
|
||||
[ext_resource type="Texture2D" uid="uid://dt0ctlr32stnn" path="res://addons/proton_scatter/icons/local.svg" id="13_txjs8"]
|
||||
[ext_resource type="PackedScene" uid="uid://c36gqn03pvlnr" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/header/parameter_spinbox.tscn" id="13_vhfch"]
|
||||
[ext_resource type="Texture2D" uid="uid://p2v2cqm7k60o" path="res://addons/proton_scatter/icons/restrict_volume_lock.svg" id="15_0w0as"]
|
||||
[ext_resource type="Texture2D" uid="uid://71efqwg3d70v" path="res://addons/proton_scatter/icons/global.svg" id="16_ocvvf"]
|
||||
[ext_resource type="PackedScene" uid="uid://bspbhkrpgak0e" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/parameter_scalar.tscn" id="17_aoulv"]
|
||||
[ext_resource type="Texture2D" uid="uid://vxd0iun0wq8i" path="res://addons/proton_scatter/icons/individual_instances.svg" id="19_ln8a3"]
|
||||
|
||||
[node name="ModifierPanel" type="MarginContainer"]
|
||||
anchors_preset = 10
|
||||
anchor_right = 1.0
|
||||
grow_horizontal = 2
|
||||
theme_type_variation = &"fg"
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="Panel" type="Panel" parent="."]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
theme_override_constants/margin_left = 4
|
||||
theme_override_constants/margin_top = 4
|
||||
theme_override_constants/margin_right = 4
|
||||
theme_override_constants/margin_bottom = 4
|
||||
metadata/_edit_layout_mode = 1
|
||||
metadata/_edit_use_custom_anchors = false
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
|
||||
[node name="Expand" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Toggle the parameters view"
|
||||
focus_mode = 0
|
||||
mouse_filter = 1
|
||||
toggle_mode = true
|
||||
icon = ExtResource("2_2djuo")
|
||||
flat = true
|
||||
icon_alignment = 1
|
||||
script = ExtResource("4")
|
||||
default_icon = ExtResource("2_2djuo")
|
||||
pressed_icon = ExtResource("4_7nlfc")
|
||||
|
||||
[node name="ModifierName" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "ModifierPanel"
|
||||
vertical_alignment = 1
|
||||
clip_text = true
|
||||
|
||||
[node name="Buttons" type="HBoxContainer" parent="MarginContainer/VBoxContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="Warning" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer/Buttons"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
focus_mode = 0
|
||||
mouse_filter = 1
|
||||
icon = ExtResource("9")
|
||||
flat = true
|
||||
|
||||
[node name="MenuButton" type="MenuButton" parent="MarginContainer/VBoxContainer/HBoxContainer/Buttons"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Show options"
|
||||
icon = ExtResource("6_lmo8k")
|
||||
item_count = 4
|
||||
popup/item_0/text = "Show documentation"
|
||||
popup/item_0/icon = ExtResource("7_owhij")
|
||||
popup/item_0/id = 0
|
||||
popup/item_1/text = ""
|
||||
popup/item_1/id = 1
|
||||
popup/item_1/separator = true
|
||||
popup/item_2/text = "Duplicate"
|
||||
popup/item_2/icon = ExtResource("7_f6nan")
|
||||
popup/item_2/id = 2
|
||||
popup/item_3/text = "Delete"
|
||||
popup/item_3/icon = ExtResource("5")
|
||||
popup/item_3/id = 3
|
||||
|
||||
[node name="Enabled" type="CheckBox" parent="MarginContainer/VBoxContainer/HBoxContainer/Buttons"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Toggle the modifier.
|
||||
|
||||
If the modifier is disabled, it will not contribute to the final result but will still remain in the stack.
|
||||
|
||||
Use this feature to quickly see how the modifier affects the overall stack."
|
||||
focus_mode = 0
|
||||
mouse_filter = 1
|
||||
|
||||
[node name="Remove" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer/Buttons"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
tooltip_text = "Delete the modifier.
|
||||
This will remove it from the stack."
|
||||
focus_mode = 0
|
||||
mouse_filter = 1
|
||||
icon = ExtResource("5")
|
||||
flat = true
|
||||
icon_alignment = 1
|
||||
|
||||
[node name="VSeparator" type="VSeparator" parent="MarginContainer/VBoxContainer/HBoxContainer/Buttons"]
|
||||
modulate = Color(1, 1, 1, 0.54902)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="DragControl" type="TextureRect" parent="MarginContainer/VBoxContainer/HBoxContainer/Buttons"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Drag and move this button to change the stack order.
|
||||
|
||||
Modifiers are processed from top to bottom."
|
||||
mouse_default_cursor_shape = 6
|
||||
texture = ExtResource("9_t6pse")
|
||||
stretch_mode = 3
|
||||
|
||||
[node name="ParametersContainer" type="MarginContainer" parent="MarginContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 3
|
||||
theme_override_constants/margin_top = 3
|
||||
theme_override_constants/margin_right = 3
|
||||
theme_override_constants/margin_bottom = 3
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/VBoxContainer/ParametersContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="ParametersRoot" type="VBoxContainer" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="CommonHeader" type="HBoxContainer" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
alignment = 1
|
||||
|
||||
[node name="ExpandButton" type="MarginContainer" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader"]
|
||||
layout_mode = 2
|
||||
script = ExtResource("10_ptukr")
|
||||
|
||||
[node name="OverrideGlobalSeed" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader/ExpandButton" instance=ExtResource("11_y7srw")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Button" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader/ExpandButton/OverrideGlobalSeed" index="0"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Random seed.
|
||||
|
||||
Enable to force a custom seed on this modifier only. If this option is disabled, the Global Seed from the ProtonScatter node will be used instead."
|
||||
icon = ExtResource("11_qwhro")
|
||||
icon_alignment = 0
|
||||
default_icon = ExtResource("11_qwhro")
|
||||
pressed_icon = ExtResource("11_qwhro")
|
||||
|
||||
[node name="SpinBoxRoot" type="HBoxContainer" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader/ExpandButton"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="VSeparator" type="VSeparator" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader/ExpandButton/SpinBoxRoot"]
|
||||
modulate = Color(1, 1, 1, 0)
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
theme_override_constants/separation = 28
|
||||
|
||||
[node name="CustomSeed" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader/ExpandButton/SpinBoxRoot" instance=ExtResource("13_vhfch")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Control" type="Control" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="RestrictHeight" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader" instance=ExtResource("11_y7srw")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 1
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Button" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader/RestrictHeight" index="0"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Restrict height.
|
||||
|
||||
If enabled, the modifier will try to remain in the local XZ plane instead of using the full volume defined by the ScatterShapes."
|
||||
icon = ExtResource("12_lx60d")
|
||||
default_icon = ExtResource("12_lx60d")
|
||||
pressed_icon = ExtResource("15_0w0as")
|
||||
|
||||
[node name="TransformSpace" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader" instance=ExtResource("17_aoulv")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader/TransformSpace" index="0"]
|
||||
visible = false
|
||||
text = ""
|
||||
|
||||
[node name="SpinBox" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader/TransformSpace/MarginContainer/MarginContainer" index="0"]
|
||||
visible = false
|
||||
|
||||
[node name="OptionButton" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader/TransformSpace/MarginContainer/MarginContainer" index="1"]
|
||||
visible = true
|
||||
item_count = 3
|
||||
fit_to_longest_item = false
|
||||
popup/item_0/text = "Global"
|
||||
popup/item_0/icon = ExtResource("16_ocvvf")
|
||||
popup/item_0/id = 0
|
||||
popup/item_1/text = "Local"
|
||||
popup/item_1/icon = ExtResource("13_txjs8")
|
||||
popup/item_1/id = 1
|
||||
popup/item_2/text = "Individual"
|
||||
popup/item_2/icon = ExtResource("19_ln8a3")
|
||||
popup/item_2/id = 2
|
||||
|
||||
[node name="WarningDialog" type="AcceptDialog" parent="."]
|
||||
title = "Warning"
|
||||
unresizable = true
|
||||
popup_window = true
|
||||
|
||||
[editable path="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader/ExpandButton/OverrideGlobalSeed"]
|
||||
[editable path="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader/RestrictHeight"]
|
||||
[editable path="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader/TransformSpace"]
|
||||
@@ -0,0 +1,14 @@
|
||||
@tool
|
||||
extends Control
|
||||
|
||||
|
||||
@onready var _button: Button = $OverrideGlobalSeed/Button
|
||||
@onready var _spinbox_root: Control = $SpinBoxRoot
|
||||
|
||||
|
||||
func _ready():
|
||||
_button.toggled.connect(_on_toggled)
|
||||
|
||||
|
||||
func _on_toggled(enabled: bool) -> void:
|
||||
_spinbox_root.visible = enabled
|
||||
@@ -0,0 +1,18 @@
|
||||
@tool
|
||||
extends Button
|
||||
|
||||
|
||||
@export var default_icon: Texture
|
||||
@export var pressed_icon: Texture
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
toggled.connect(_on_toggled)
|
||||
_on_toggled(button_pressed)
|
||||
|
||||
|
||||
func _on_toggled(pressed: bool) -> void:
|
||||
if pressed:
|
||||
icon = pressed_icon
|
||||
else:
|
||||
icon = default_icon
|
||||
@@ -0,0 +1,9 @@
|
||||
@tool
|
||||
extends VBoxContainer
|
||||
|
||||
|
||||
@onready var label: Label = $Header/Label
|
||||
|
||||
|
||||
func set_category_name(text) -> void:
|
||||
label.text = text
|
||||
@@ -0,0 +1,26 @@
|
||||
[gd_scene load_steps=2 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier_list_popup/category.gd" id="1"]
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer"]
|
||||
margin_right = 40.0
|
||||
margin_bottom = 40.0
|
||||
rect_pivot_offset = Vector2( -591.851, -77.5574 )
|
||||
size_flags_horizontal = 3
|
||||
script = ExtResource( 1 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Header" type="VBoxContainer" parent="."]
|
||||
margin_right = 40.0
|
||||
margin_bottom = 22.0
|
||||
|
||||
[node name="Label" type="Label" parent="Header"]
|
||||
margin_right = 40.0
|
||||
margin_bottom = 14.0
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="Header"]
|
||||
margin_top = 18.0
|
||||
margin_right = 40.0
|
||||
margin_bottom = 22.0
|
||||
@@ -0,0 +1,113 @@
|
||||
@tool
|
||||
extends PopupPanel
|
||||
|
||||
|
||||
signal add_modifier
|
||||
|
||||
var _modifiers := []
|
||||
|
||||
@onready var _category_root: Control = $MarginContainer/CategoryRoot
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_rebuild_ui()
|
||||
|
||||
|
||||
func _rebuild_ui():
|
||||
for c in _category_root.get_children():
|
||||
c.queue_free()
|
||||
|
||||
_discover_modifiers()
|
||||
for modifier in _modifiers:
|
||||
var instance = modifier.new()
|
||||
if instance.enabled:
|
||||
var category = _get_or_create_category(instance.category)
|
||||
var button = _create_button(instance.display_name)
|
||||
category.add_child(button, true)
|
||||
button.pressed.connect(_on_pressed.bind(modifier))
|
||||
|
||||
for category in _category_root.get_children():
|
||||
var header = category.get_child(0)
|
||||
_sort_children_by_name(category)
|
||||
category.move_child(header, 0)
|
||||
|
||||
|
||||
func _create_button(display_name) -> Button:
|
||||
var button = Button.new()
|
||||
button.name = display_name
|
||||
button.text = display_name
|
||||
button.alignment = HORIZONTAL_ALIGNMENT_LEFT
|
||||
return button
|
||||
|
||||
|
||||
func _sort_children_by_name(node: Node) -> void:
|
||||
var dict := {}
|
||||
var names := []
|
||||
|
||||
for child in node.get_children():
|
||||
names.push_back(child.name)
|
||||
dict[child.name] = child
|
||||
|
||||
names.sort_custom(func(a, b): return String(a) < String(b))
|
||||
|
||||
for i in names.size():
|
||||
var n = names[i]
|
||||
node.move_child(dict[n], i)
|
||||
|
||||
|
||||
func _get_or_create_category(text: String) -> Control:
|
||||
if _category_root.has_node(text):
|
||||
return _category_root.get_node(text) as Control
|
||||
|
||||
var c = preload("category.tscn").instantiate()
|
||||
c.name = text
|
||||
_category_root.add_child(c, true)
|
||||
c.set_category_name(text)
|
||||
return c
|
||||
|
||||
|
||||
func _discover_modifiers() -> void:
|
||||
if _modifiers.is_empty():
|
||||
var path = _get_root_folder() + "/src/modifiers/"
|
||||
_discover_modifiers_recursive(path)
|
||||
|
||||
|
||||
func _discover_modifiers_recursive(path) -> void:
|
||||
var dir = DirAccess.open(path)
|
||||
dir.list_dir_begin()
|
||||
var path_root = dir.get_current_dir() + "/"
|
||||
|
||||
while true:
|
||||
var file = dir.get_next()
|
||||
if file == "":
|
||||
break
|
||||
if file == "base_modifier.gd":
|
||||
continue
|
||||
if dir.current_is_dir():
|
||||
_discover_modifiers_recursive(path_root + file)
|
||||
continue
|
||||
if not file.ends_with(".gd") and not file.ends_with(".gdc"):
|
||||
continue
|
||||
|
||||
var full_path = path_root + file
|
||||
var script = load(full_path)
|
||||
if not script or not script.can_instantiate():
|
||||
print("Error: Failed to load script ", file)
|
||||
continue
|
||||
|
||||
_modifiers.push_back(script)
|
||||
|
||||
dir.list_dir_end()
|
||||
|
||||
|
||||
func _get_root_folder() -> String:
|
||||
var script: Script = get_script()
|
||||
var path: String = script.get_path().get_base_dir()
|
||||
var folders = path.right(-6) # Remove the res://
|
||||
var tokens = folders.split('/')
|
||||
return "res://" + tokens[0] + "/" + tokens[1]
|
||||
|
||||
|
||||
func _on_pressed(modifier) -> void:
|
||||
add_modifier.emit(modifier.new())
|
||||
visible = false
|
||||
@@ -0,0 +1,20 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://belutr5odecw2"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier_list_popup/popup.gd" id="1"]
|
||||
|
||||
[node name="ModifiersPopup" type="PopupPanel"]
|
||||
size = Vector2i(597, 322)
|
||||
visible = true
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 4.0
|
||||
offset_top = 4.0
|
||||
offset_right = -431.0
|
||||
offset_bottom = -282.0
|
||||
|
||||
[node name="CategoryRoot" type="HBoxContainer" parent="MarginContainer"]
|
||||
offset_right = 589.0
|
||||
offset_bottom = 314.0
|
||||
@@ -0,0 +1,87 @@
|
||||
@tool
|
||||
extends Window
|
||||
|
||||
|
||||
signal load_preset
|
||||
signal delete_preset
|
||||
|
||||
|
||||
@onready var _no_presets: Label = $MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/NoPresets
|
||||
@onready var _root: VBoxContainer = $MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/PresetsRoot
|
||||
@onready var _confirmation_dialog: ConfirmationDialog = $ConfirmationDialog
|
||||
|
||||
var _selected: String
|
||||
var _selected_ui: Node
|
||||
|
||||
|
||||
func _ready():
|
||||
_rebuild_ui()
|
||||
about_to_popup.connect(_rebuild_ui)
|
||||
|
||||
|
||||
func _rebuild_ui():
|
||||
for c in _root.get_children():
|
||||
c.queue_free()
|
||||
_root.visible = false
|
||||
|
||||
var presets = _find_all_presets()
|
||||
if presets.empty():
|
||||
_no_presets.visible = true
|
||||
return
|
||||
|
||||
_no_presets.visible = false
|
||||
_root.visible = true
|
||||
for p in presets:
|
||||
var ui = preload("./preset.tscn").instantiate()
|
||||
_root.add_child(ui)
|
||||
ui.set_preset_name(p)
|
||||
ui.load_preset.connect(_on_load_preset.bind(p))
|
||||
ui.delete_preset.connect(_on_delete_preset.bind(p, ui))
|
||||
|
||||
|
||||
func _find_all_presets() -> Array:
|
||||
var root := _get_root_folder() + "/presets/"
|
||||
var res := []
|
||||
var dir = DirAccess.open(root)
|
||||
if not dir:
|
||||
return res
|
||||
|
||||
dir.list_dir_begin()
|
||||
while true:
|
||||
var file = dir.get_next()
|
||||
if file == "":
|
||||
break
|
||||
|
||||
if file.ends_with(".tscn"):
|
||||
res.push_back(file.get_basename())
|
||||
|
||||
dir.list_dir_end()
|
||||
res.sort()
|
||||
return res
|
||||
|
||||
|
||||
func _get_root_folder() -> String:
|
||||
var path: String = get_script().get_path().get_base_dir()
|
||||
var folders = path.right(6) # Remove the res://
|
||||
var tokens = folders.split('/')
|
||||
return "res://" + tokens[0] + "/" + tokens[1]
|
||||
|
||||
|
||||
func _on_load_preset(preset_name) -> void:
|
||||
emit_signal("load_preset", preset_name)
|
||||
visible = false
|
||||
|
||||
|
||||
func _on_delete_preset(preset_name, ui) -> void:
|
||||
_selected = preset_name
|
||||
_selected_ui = ui
|
||||
_confirmation_dialog.popup_centered()
|
||||
|
||||
|
||||
func _on_delete_preset_confirmed():
|
||||
DirAccess.remove_absolute(_get_root_folder() + "/presets/" + _selected + ".tscn")
|
||||
_selected_ui.queue_free()
|
||||
|
||||
|
||||
func _on_cancel_pressed():
|
||||
visible = false
|
||||
@@ -0,0 +1,101 @@
|
||||
[gd_scene load_steps=2 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/presets/load_preset.gd" id="1"]
|
||||
|
||||
[node name="LoadPresetPopup" type="WindowDialog"]
|
||||
visible = true
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
margin_left = -147.5
|
||||
margin_top = -156.5
|
||||
margin_right = 147.5
|
||||
margin_bottom = 156.5
|
||||
size_flags_horizontal = 5
|
||||
size_flags_vertical = 5
|
||||
window_title = "Load Presets"
|
||||
resizable = true
|
||||
script = ExtResource( 1 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
custom_constants/margin_right = 8
|
||||
custom_constants/margin_top = 8
|
||||
custom_constants/margin_left = 8
|
||||
custom_constants/margin_bottom = 8
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
|
||||
margin_left = 8.0
|
||||
margin_top = 8.0
|
||||
margin_right = 287.0
|
||||
margin_bottom = 305.0
|
||||
custom_constants/separation = 6
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="MarginContainer/VBoxContainer"]
|
||||
margin_right = 279.0
|
||||
margin_bottom = 271.0
|
||||
size_flags_vertical = 3
|
||||
scroll_horizontal_enabled = false
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/VBoxContainer/ScrollContainer"]
|
||||
margin_right = 279.0
|
||||
margin_bottom = 271.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="NoPresets" type="Label" parent="MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer"]
|
||||
visible = false
|
||||
margin_right = 247.0
|
||||
margin_bottom = 118.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
text = "No presets found.
|
||||
|
||||
Create new presets by pressing the \"Save Preset\" button first."
|
||||
valign = 1
|
||||
autowrap = true
|
||||
|
||||
[node name="PresetsRoot" type="VBoxContainer" parent="MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer"]
|
||||
margin_right = 279.0
|
||||
margin_bottom = 271.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"]
|
||||
margin_left = 112.0
|
||||
margin_top = 277.0
|
||||
margin_right = 166.0
|
||||
margin_bottom = 297.0
|
||||
size_flags_horizontal = 4
|
||||
|
||||
[node name="CancelButton" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer"]
|
||||
margin_right = 54.0
|
||||
margin_bottom = 20.0
|
||||
text = "Cancel"
|
||||
|
||||
[node name="ConfirmationDialog" type="ConfirmationDialog" parent="."]
|
||||
visible = true
|
||||
margin_left = -320.0
|
||||
margin_top = 37.0
|
||||
margin_right = -120.0
|
||||
margin_bottom = 112.0
|
||||
dialog_text = "Delete preset?
|
||||
(This action can't be undone)"
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[connection signal="pressed" from="MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/PresetsRoot/Preset" to="MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/PresetsRoot/Preset" method="_on_pressed"]
|
||||
[connection signal="pressed" from="MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/PresetsRoot/Preset2" to="MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/PresetsRoot/Preset2" method="_on_pressed"]
|
||||
[connection signal="pressed" from="MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/PresetsRoot/Preset3" to="MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/PresetsRoot/Preset3" method="_on_pressed"]
|
||||
[connection signal="pressed" from="MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/PresetsRoot/Preset4" to="MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/PresetsRoot/Preset4" method="_on_pressed"]
|
||||
[connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer/CancelButton" to="." method="_on_cancel_pressed"]
|
||||
[connection signal="confirmed" from="ConfirmationDialog" to="." method="_on_delete_preset_confirmed"]
|
||||
@@ -0,0 +1,21 @@
|
||||
@tool
|
||||
extends Button
|
||||
|
||||
|
||||
signal load_preset
|
||||
signal delete_preset
|
||||
|
||||
|
||||
@onready var _label: Label = $MarginContainer/HBoxContainer/Label
|
||||
|
||||
|
||||
func set_preset_name(text) -> void:
|
||||
_label.text = text
|
||||
|
||||
|
||||
func _on_pressed() -> void:
|
||||
load_preset.emit()
|
||||
|
||||
|
||||
func _on_delete() -> void:
|
||||
delete_preset.emit()
|
||||
@@ -0,0 +1,54 @@
|
||||
[gd_scene load_steps=3 format=3]
|
||||
|
||||
[ext_resource type="Texture" uid="uid://dahwdjl2er75o" path="res://addons/proton_scatter/icons/close.svg" id="1"]
|
||||
[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/presets/preset.gd" id="2"]
|
||||
|
||||
[node name="Preset" type="Button"]
|
||||
anchor_top = 0.5
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 0.5
|
||||
margin_top = -10.0
|
||||
margin_bottom = 29.0
|
||||
rect_min_size = Vector2( 0, 40 )
|
||||
focus_mode = 0
|
||||
script = ExtResource( 2 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
mouse_filter = 2
|
||||
custom_constants/margin_right = 6
|
||||
custom_constants/margin_top = 6
|
||||
custom_constants/margin_left = 6
|
||||
custom_constants/margin_bottom = 6
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer"]
|
||||
margin_left = 6.0
|
||||
margin_top = 6.0
|
||||
margin_right = 1274.0
|
||||
margin_bottom = 34.0
|
||||
|
||||
[node name="Label" type="Label" parent="MarginContainer/HBoxContainer"]
|
||||
margin_top = 7.0
|
||||
margin_right = 1236.0
|
||||
margin_bottom = 21.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Preset name"
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Delete" type="Button" parent="MarginContainer/HBoxContainer"]
|
||||
margin_left = 1240.0
|
||||
margin_right = 1268.0
|
||||
margin_bottom = 28.0
|
||||
icon = ExtResource( 1 )
|
||||
|
||||
[connection signal="pressed" from="." to="." method="_on_pressed"]
|
||||
[connection signal="pressed" from="MarginContainer/HBoxContainer/Delete" to="." method="_on_delete"]
|
||||
@@ -0,0 +1,78 @@
|
||||
@tool
|
||||
extends Window
|
||||
|
||||
|
||||
signal save_preset
|
||||
|
||||
|
||||
@onready var _line_edit: LineEdit = $MarginContainer/VBoxContainer/LineEdit
|
||||
@onready var _cancel: Button = $MarginContainer/VBoxContainer/HBoxContainer/Cancel
|
||||
@onready var _save: Button = $MarginContainer/VBoxContainer/HBoxContainer/Save
|
||||
@onready var _warning: Label = $MarginContainer/VBoxContainer/Warning
|
||||
@onready var _confirm_overwrite = $ConfirmationDialog
|
||||
|
||||
|
||||
func _ready():
|
||||
_cancel.pressed.connect(_on_cancel_pressed)
|
||||
_save.pressed.connect(_on_save_pressed)
|
||||
_warning.text = ""
|
||||
_confirm_overwrite.confirmed.connect(_save_preset)
|
||||
|
||||
|
||||
func _on_cancel_pressed() -> void:
|
||||
visible = false
|
||||
_line_edit.text = ""
|
||||
|
||||
|
||||
func _on_save_pressed() -> void:
|
||||
var preset_name: String = _line_edit.text
|
||||
if preset_name.is_empty():
|
||||
_warning.text = "Preset name can't be empty"
|
||||
return
|
||||
|
||||
if not preset_name.is_valid_filename():
|
||||
_warning.text = """Preset name must be a valid file name.
|
||||
It cannot contain the following characters:
|
||||
: / \\ ? * " | % < >"""
|
||||
return
|
||||
|
||||
_warning.text = ""
|
||||
if _exists(preset_name):
|
||||
_confirm_overwrite.dialog_text = "Preset \"" + preset_name + "\" already exists. Overwrite?"
|
||||
_confirm_overwrite.popup_centered()
|
||||
else:
|
||||
_save_preset()
|
||||
|
||||
|
||||
func _save_preset() -> void:
|
||||
emit_signal("save_preset", _line_edit.text)
|
||||
visible = false
|
||||
_line_edit.text = ""
|
||||
|
||||
|
||||
func _exists(preset: String) -> bool:
|
||||
var dir = DirAccess.open(_get_root_folder() + "/presets/")
|
||||
if not dir:
|
||||
return false
|
||||
|
||||
dir.list_dir_begin()
|
||||
|
||||
while true:
|
||||
var file = dir.get_next()
|
||||
if file == "":
|
||||
break
|
||||
|
||||
if file == preset + ".tscn":
|
||||
dir.list_dir_end()
|
||||
return true
|
||||
|
||||
dir.list_dir_end()
|
||||
return false
|
||||
|
||||
|
||||
func _get_root_folder() -> String:
|
||||
var script: Script = get_script()
|
||||
var path: String = script.get_path().get_base_dir()
|
||||
var folders = path.right(6) # Remove the res://
|
||||
var tokens = folders.split('/')
|
||||
return "res://" + tokens[0] + "/" + tokens[1]
|
||||
@@ -0,0 +1,96 @@
|
||||
[gd_scene load_steps=2 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/presets/save_preset.gd" id="1"]
|
||||
|
||||
[node name="SavePresetPopup" type="WindowDialog"]
|
||||
visible = true
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
margin_left = -180.0
|
||||
margin_top = -81.0
|
||||
margin_right = 180.0
|
||||
margin_bottom = 81.0
|
||||
size_flags_horizontal = 5
|
||||
size_flags_vertical = 5
|
||||
window_title = "Save Preset"
|
||||
resizable = true
|
||||
script = ExtResource( 1 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
custom_constants/margin_right = 24
|
||||
custom_constants/margin_top = 4
|
||||
custom_constants/margin_left = 24
|
||||
custom_constants/margin_bottom = 24
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
|
||||
margin_left = 24.0
|
||||
margin_top = 4.0
|
||||
margin_right = 336.0
|
||||
margin_bottom = 120.0
|
||||
size_flags_vertical = 0
|
||||
custom_constants/separation = 12
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Warning" type="Label" parent="MarginContainer/VBoxContainer"]
|
||||
modulate = Color( 1, 0.513726, 0.278431, 1 )
|
||||
margin_right = 312.0
|
||||
margin_bottom = 48.0
|
||||
text = "Preset name must be a valid file name.
|
||||
It cannot contain the following characters:
|
||||
: / \\\\ ? * \" | % < >"
|
||||
valign = 2
|
||||
autowrap = true
|
||||
|
||||
[node name="LineEdit" type="LineEdit" parent="MarginContainer/VBoxContainer"]
|
||||
margin_top = 60.0
|
||||
margin_right = 312.0
|
||||
margin_bottom = 84.0
|
||||
placeholder_text = "Preset Name"
|
||||
caret_blink = true
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"]
|
||||
margin_top = 96.0
|
||||
margin_right = 312.0
|
||||
margin_bottom = 116.0
|
||||
custom_constants/separation = 24
|
||||
alignment = 1
|
||||
|
||||
[node name="Cancel" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer"]
|
||||
margin_left = 96.0
|
||||
margin_right = 150.0
|
||||
margin_bottom = 20.0
|
||||
text = "Cancel"
|
||||
|
||||
[node name="Save" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer"]
|
||||
margin_left = 174.0
|
||||
margin_right = 215.0
|
||||
margin_bottom = 20.0
|
||||
text = "Save"
|
||||
|
||||
[node name="ConfirmationDialog" type="ConfirmationDialog" parent="."]
|
||||
visible = true
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
margin_left = -135.0
|
||||
margin_top = -44.0
|
||||
margin_right = 135.0
|
||||
margin_bottom = 44.0
|
||||
window_title = "Overwrite existing preset?"
|
||||
dialog_autowrap = true
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
@tool
|
||||
extends Control
|
||||
|
||||
|
||||
const ModifierPanel := preload("./modifier/modifier_panel.tscn")
|
||||
|
||||
|
||||
@onready var _modifiers_container: Control = $%ModifiersContainer
|
||||
@onready var _modifiers_popup: PopupPanel = $%ModifiersPopup
|
||||
|
||||
var _scatter
|
||||
var _modifier_stack
|
||||
var _undo_redo
|
||||
var _is_ready := false
|
||||
|
||||
|
||||
func _ready():
|
||||
_modifiers_popup.add_modifier.connect(_on_modifier_added)
|
||||
_modifiers_container.child_moved.connect(_on_modifier_moved)
|
||||
%Rebuild.pressed.connect(_on_rebuild_pressed)
|
||||
%DocumentationButton.pressed.connect(_on_documentation_requested.bind("ProtonScatter"))
|
||||
%LoadPreset.pressed.connect(_on_load_preset_pressed)
|
||||
%SavePreset.pressed.connect(_on_save_preset_pressed)
|
||||
|
||||
_is_ready = true
|
||||
rebuild_ui()
|
||||
|
||||
|
||||
func set_node(node) -> void:
|
||||
if not node:
|
||||
return
|
||||
|
||||
_scatter = node
|
||||
_undo_redo = _scatter.undo_redo
|
||||
%Documentation.set_editor_plugin(_scatter.editor_plugin)
|
||||
%Presets.set_editor_plugin(_scatter.editor_plugin)
|
||||
rebuild_ui()
|
||||
|
||||
|
||||
func rebuild_ui() -> void:
|
||||
if not _is_ready:
|
||||
return
|
||||
|
||||
_validate_stack_connections()
|
||||
_clear()
|
||||
for m in _modifier_stack.stack:
|
||||
var ui = ModifierPanel.instantiate()
|
||||
_modifiers_container.add_child(ui)
|
||||
ui.set_root(_scatter)
|
||||
ui.create_ui_for(m)
|
||||
ui.removed.connect(_on_modifier_removed.bind(m))
|
||||
ui.value_changed.connect(_on_value_changed)
|
||||
ui.documentation_requested.connect(_on_documentation_requested.bind(m.display_name))
|
||||
ui.duplication_requested.connect(_on_modifier_duplicated.bind(m))
|
||||
|
||||
|
||||
func _clear() -> void:
|
||||
for c in _modifiers_container.get_children():
|
||||
_modifiers_container.remove_child(c)
|
||||
c.queue_free()
|
||||
|
||||
|
||||
func _validate_stack_connections() -> void:
|
||||
if not _scatter:
|
||||
return
|
||||
|
||||
if _modifier_stack:
|
||||
_modifier_stack.stack_changed.disconnect(_on_stack_changed)
|
||||
|
||||
_modifier_stack = _scatter.modifier_stack
|
||||
_modifier_stack.stack_changed.connect(_on_stack_changed)
|
||||
|
||||
if _modifier_stack.just_created:
|
||||
%Presets.load_default(_scatter)
|
||||
_modifier_stack.just_created = false
|
||||
rebuild_ui()
|
||||
|
||||
|
||||
func _set_children_owner(new_owner: Node, node: Node):
|
||||
for child in node.get_children():
|
||||
child.set_owner(new_owner)
|
||||
if child.get_children().size() > 0:
|
||||
_set_children_owner(new_owner, child)
|
||||
|
||||
|
||||
func _get_root_folder() -> String:
|
||||
var path: String = get_script().get_path().get_base_dir()
|
||||
var folders = path.right(6) # Remove the res://
|
||||
var tokens = folders.split('/')
|
||||
return "res://" + tokens[0] + "/" + tokens[1]
|
||||
|
||||
|
||||
func _on_modifier_added(modifier) -> void:
|
||||
if _undo_redo:
|
||||
_undo_redo.create_action("Create modifier " + modifier.display_name)
|
||||
_undo_redo.add_undo_method(_modifier_stack, "remove", modifier)
|
||||
_undo_redo.add_do_method(_modifier_stack, "add", modifier)
|
||||
_undo_redo.commit_action()
|
||||
else:
|
||||
_modifier_stack.add(modifier)
|
||||
|
||||
|
||||
func _on_modifier_moved(old_index: int, new_index: int) -> void:
|
||||
if _undo_redo:
|
||||
_undo_redo.create_action("Move modifier")
|
||||
_undo_redo.add_undo_method(_modifier_stack, "move", new_index, old_index)
|
||||
_undo_redo.add_do_method(_modifier_stack, "move", old_index, new_index)
|
||||
_undo_redo.commit_action()
|
||||
else:
|
||||
_modifier_stack.move(old_index, new_index)
|
||||
|
||||
|
||||
func _on_modifier_removed(modifier) -> void:
|
||||
if _undo_redo:
|
||||
_undo_redo.create_action("Remove modifier " + modifier.display_name)
|
||||
_undo_redo.add_undo_method(_modifier_stack, "add", modifier)
|
||||
_undo_redo.add_do_method(_modifier_stack, "remove", modifier)
|
||||
_undo_redo.commit_action()
|
||||
else:
|
||||
_modifier_stack.remove(modifier)
|
||||
|
||||
|
||||
func _on_modifier_duplicated(modifier) -> void:
|
||||
var index = _modifier_stack.get_index(modifier)
|
||||
if index == -1:
|
||||
return
|
||||
|
||||
if _undo_redo:
|
||||
_undo_redo.create_action("Duplicate modifier " + modifier.display_name)
|
||||
_undo_redo.add_undo_method(_modifier_stack, "remove_at", index + 1)
|
||||
_undo_redo.add_do_method(_modifier_stack, "duplicate_modifier", modifier)
|
||||
_undo_redo.commit_action()
|
||||
else:
|
||||
_modifier_stack.duplicate_modifier(modifier)
|
||||
|
||||
|
||||
func _on_stack_changed() -> void:
|
||||
rebuild_ui()
|
||||
|
||||
|
||||
func _on_value_changed() -> void:
|
||||
_modifier_stack.value_changed.emit()
|
||||
|
||||
|
||||
func _on_rebuild_pressed() -> void:
|
||||
if _scatter:
|
||||
_scatter.full_rebuild()
|
||||
|
||||
|
||||
func _on_save_preset_pressed() -> void:
|
||||
%Presets.save_preset(_scatter)
|
||||
|
||||
|
||||
func _on_load_preset_pressed() -> void:
|
||||
%Presets.load_preset(_scatter)
|
||||
|
||||
|
||||
func _on_documentation_requested(page_name) -> void:
|
||||
%Documentation.show_page(page_name)
|
||||
@@ -0,0 +1,111 @@
|
||||
[gd_scene load_steps=12 format=3 uid="uid://dllpt2dven8vw"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://cun73k8jdmr4e" path="res://addons/proton_scatter/icons/add.svg" id="1_4vwtj"]
|
||||
[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/stack_panel.gd" id="1_ga4or"]
|
||||
[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/drag_container.gd" id="1_hg5ys"]
|
||||
[ext_resource type="Texture2D" uid="uid://yqlpvcmb7mfi" path="res://addons/proton_scatter/icons/rebuild.svg" id="2_svid4"]
|
||||
[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/add_modifier_button.gd" id="3_01ldn"]
|
||||
[ext_resource type="PackedScene" uid="uid://belutr5odecw2" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier_list_popup/popup.tscn" id="3_pkswu"]
|
||||
[ext_resource type="Texture2D" uid="uid://ddjrq1h4mkn6a" path="res://addons/proton_scatter/icons/load.svg" id="3_w72lv"]
|
||||
[ext_resource type="Texture2D" uid="uid://b2omj2e03x72e" path="res://addons/proton_scatter/icons/save.svg" id="4_5l2cx"]
|
||||
[ext_resource type="Texture2D" uid="uid://do8d3urxirjoa" path="res://addons/proton_scatter/icons/doc.svg" id="8_fgqhd"]
|
||||
[ext_resource type="PackedScene" uid="uid://cfg8iqtuion8b" path="res://addons/proton_scatter/src/documentation/documentation.tscn" id="9_y57kc"]
|
||||
[ext_resource type="PackedScene" uid="uid://bcsosdvstytoq" path="res://addons/proton_scatter/src/presets/presets.tscn" id="11_2ut8s"]
|
||||
|
||||
[node name="StackPanel" type="MarginContainer"]
|
||||
clip_children = 1
|
||||
clip_contents = true
|
||||
offset_right = 456.0
|
||||
offset_bottom = 144.0
|
||||
theme_override_constants/margin_left = 4
|
||||
theme_override_constants/margin_top = 4
|
||||
theme_override_constants/margin_right = 4
|
||||
theme_override_constants/margin_bottom = 4
|
||||
script = ExtResource("1_ga4or")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 16
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Add" type="Button" parent="VBoxContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 2
|
||||
focus_mode = 0
|
||||
toggle_mode = true
|
||||
text = " Add modifier"
|
||||
icon = ExtResource("1_4vwtj")
|
||||
script = ExtResource("3_01ldn")
|
||||
|
||||
[node name="ModifiersPopup" parent="VBoxContainer/HBoxContainer/Add" instance=ExtResource("3_pkswu")]
|
||||
unique_name_in_owner = true
|
||||
size = Vector2i(755, 322)
|
||||
visible = false
|
||||
|
||||
[node name="Rebuild" type="Button" parent="VBoxContainer/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
tooltip_text = "Force rebuild.
|
||||
|
||||
If the placed items does not look as expected, you can press this button to force it to regenerate the result.
|
||||
|
||||
Usually, you shouldn't have to use it so please report it on Github if you found a case where it's necessary to click this. "
|
||||
focus_mode = 0
|
||||
icon = ExtResource("2_svid4")
|
||||
icon_alignment = 1
|
||||
|
||||
[node name="VSeparator" type="VSeparator" parent="VBoxContainer/HBoxContainer"]
|
||||
modulate = Color(1, 1, 1, 0.54902)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="LoadPreset" type="Button" parent="VBoxContainer/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Load a preset."
|
||||
focus_mode = 0
|
||||
text = "
|
||||
"
|
||||
icon = ExtResource("3_w72lv")
|
||||
icon_alignment = 1
|
||||
|
||||
[node name="SavePreset" type="Button" parent="VBoxContainer/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Save a preset."
|
||||
focus_mode = 0
|
||||
text = "
|
||||
"
|
||||
icon = ExtResource("4_5l2cx")
|
||||
icon_alignment = 1
|
||||
|
||||
[node name="VSeparator2" type="VSeparator" parent="VBoxContainer/HBoxContainer"]
|
||||
modulate = Color(1, 1, 1, 0.54902)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="DocumentationButton" type="Button" parent="VBoxContainer/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Open documentation"
|
||||
focus_mode = 0
|
||||
text = "
|
||||
"
|
||||
icon = ExtResource("8_fgqhd")
|
||||
icon_alignment = 1
|
||||
|
||||
[node name="ModifiersContainer" type="Container" parent="VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
clip_children = 1
|
||||
custom_minimum_size = Vector2(0, -4)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
mouse_filter = 0
|
||||
script = ExtResource("1_hg5ys")
|
||||
|
||||
[node name="Documentation" parent="." instance=ExtResource("9_y57kc")]
|
||||
unique_name_in_owner = true
|
||||
|
||||
[node name="Presets" parent="." instance=ExtResource("11_2ut8s")]
|
||||
unique_name_in_owner = true
|
||||
96
addons/proton_scatter/src/stack/modifier_stack.gd
Normal file
96
addons/proton_scatter/src/stack/modifier_stack.gd
Normal file
@@ -0,0 +1,96 @@
|
||||
@tool
|
||||
extends Resource
|
||||
|
||||
|
||||
signal stack_changed
|
||||
signal value_changed
|
||||
signal transforms_ready
|
||||
|
||||
|
||||
const ProtonScatter := preload("../scatter.gd")
|
||||
const TransformList := preload("../common/transform_list.gd")
|
||||
|
||||
|
||||
@export var stack: Array[ScatterBaseModifier] = []
|
||||
|
||||
var just_created := false
|
||||
|
||||
|
||||
func start_update(scatter_node: ProtonScatter, domain):
|
||||
var transforms = TransformList.new()
|
||||
|
||||
for modifier in stack:
|
||||
await modifier.process_transforms(transforms, domain, scatter_node.global_seed)
|
||||
|
||||
transforms_ready.emit(transforms)
|
||||
return transforms
|
||||
|
||||
|
||||
func stop_update() -> void:
|
||||
for modifier in stack:
|
||||
modifier.interrupt()
|
||||
|
||||
|
||||
func add(modifier: ScatterBaseModifier) -> void:
|
||||
stack.push_back(modifier)
|
||||
modifier.modifier_changed.connect(_on_modifier_changed)
|
||||
stack_changed.emit()
|
||||
|
||||
|
||||
func move(old_index: int, new_index: int) -> void:
|
||||
var modifier = stack.pop_at(old_index)
|
||||
stack.insert(new_index, modifier)
|
||||
stack_changed.emit()
|
||||
|
||||
|
||||
func remove(modifier: ScatterBaseModifier) -> void:
|
||||
if stack.has(modifier):
|
||||
stack.erase(modifier)
|
||||
stack_changed.emit()
|
||||
|
||||
|
||||
func remove_at(index: int) -> void:
|
||||
if stack.size() > index:
|
||||
stack.remove_at(index)
|
||||
stack_changed.emit()
|
||||
|
||||
|
||||
func duplicate_modifier(modifier: ScatterBaseModifier) -> void:
|
||||
var index: int = stack.find(modifier)
|
||||
if index != -1:
|
||||
var copy = modifier.get_copy()
|
||||
add(copy)
|
||||
move(stack.size() - 1, index + 1)
|
||||
|
||||
|
||||
func get_copy():
|
||||
var copy = get_script().new()
|
||||
for modifier in stack:
|
||||
copy.add(modifier.duplicate())
|
||||
return copy
|
||||
|
||||
|
||||
func get_index(modifier: ScatterBaseModifier) -> int:
|
||||
return stack.find(modifier)
|
||||
|
||||
|
||||
func is_using_edge_data() -> bool:
|
||||
for modifier in stack:
|
||||
if modifier.use_edge_data:
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
|
||||
# Returns true if at least one modifier does not require shapes in order to work.
|
||||
# (This is the case for the "Add single item" modifier for example)
|
||||
func does_not_require_shapes() -> bool:
|
||||
for modifier in stack:
|
||||
if modifier.warning_ignore_no_shape:
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
|
||||
func _on_modifier_changed() -> void:
|
||||
stack_changed.emit()
|
||||
Reference in New Issue
Block a user