INIT
This commit is contained in:
217
addons/gdfxr/editor/EditSlider.gd
Normal file
217
addons/gdfxr/editor/EditSlider.gd
Normal file
@@ -0,0 +1,217 @@
|
||||
@tool
|
||||
extends Control
|
||||
|
||||
signal value_changed(value)
|
||||
signal value_submitted()
|
||||
|
||||
@export var value: float = 0.0 :
|
||||
set = set_value
|
||||
@export var min_value: float = 0.0
|
||||
@export var max_value: float = 1.0
|
||||
|
||||
var _line_edit: LineEdit
|
||||
|
||||
var _stylebox_normal: StyleBox
|
||||
var _stylebox_hover: StyleBox
|
||||
var _stylebox_editing: StyleBox
|
||||
var _stylebox_value: StyleBox
|
||||
|
||||
var _line_edit_just_closed := false
|
||||
var _mouse_hovering := false
|
||||
var _is_editing := false
|
||||
var _is_cancel := false
|
||||
|
||||
var _drag_start_position: Vector2
|
||||
var _drag_cancelled := true
|
||||
var _drag_dist := 0.0
|
||||
var _drag_start_factor: float
|
||||
|
||||
|
||||
func _init():
|
||||
mouse_default_cursor_shape = Control.CURSOR_HSIZE
|
||||
clip_contents = true
|
||||
focus_mode = Control.FOCUS_ALL
|
||||
|
||||
var style := StyleBoxEmpty.new()
|
||||
style.content_margin_left = 8
|
||||
style.content_margin_right = 8
|
||||
|
||||
_line_edit = LineEdit.new()
|
||||
_line_edit.set_as_top_level(true)
|
||||
_line_edit.visible = false
|
||||
_line_edit.add_theme_stylebox_override("normal", style)
|
||||
_line_edit.add_theme_stylebox_override("focus", StyleBoxEmpty.new())
|
||||
|
||||
var _ret: int
|
||||
_ret = _line_edit.focus_exited.connect(self._on_line_edit_focus_exited)
|
||||
_ret = _line_edit.text_submitted.connect(self._on_line_edit_text_entered)
|
||||
_ret = _line_edit.visibility_changed.connect(self._on_line_edit_visibility_changed)
|
||||
_ret = _line_edit.gui_input.connect(self._on_line_edit_gui_input)
|
||||
|
||||
add_child(_line_edit)
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
var font := get_theme_font("font", "LineEdit")
|
||||
var font_size := get_theme_font_size("font_size", "LineEdit")
|
||||
var color := get_theme_color("highlighted_font_color" if _mouse_hovering else "font_color", "Editor")
|
||||
var number_string := "%.3f" % value
|
||||
var number_size := font.get_string_size(number_string)
|
||||
var pos := Vector2(
|
||||
(size.x - number_size.x) / 2,
|
||||
(size.y - number_size.y) / 2 + font.get_ascent()
|
||||
)
|
||||
|
||||
var stylebox := _stylebox_editing if _is_editing else _stylebox_hover if _mouse_hovering else _stylebox_normal
|
||||
|
||||
if _line_edit.visible:
|
||||
draw_style_box(stylebox, Rect2(Vector2.ZERO, size))
|
||||
else:
|
||||
var value_width := size.x * ((value - min_value) / (max_value - min_value))
|
||||
draw_style_box(stylebox, Rect2(value_width, 0, size.x - value_width, size.y))
|
||||
draw_style_box(_stylebox_value, Rect2(0, 0, value_width, size.y))
|
||||
draw_string(font, pos, number_string, HORIZONTAL_ALIGNMENT_CENTER, -1, font_size, color)
|
||||
|
||||
|
||||
func _get_minimum_size() -> Vector2:
|
||||
var ms := _stylebox_normal.get_minimum_size() if _stylebox_normal else Vector2.ZERO
|
||||
ms.y += get_theme_font("font", "LineEdit").get_height()
|
||||
return ms
|
||||
|
||||
|
||||
func _gui_input(event: InputEvent) -> void:
|
||||
var mb := event as InputEventMouseButton
|
||||
if mb and mb.button_index == MOUSE_BUTTON_LEFT:
|
||||
if mb.pressed:
|
||||
_drag_prepare(mb)
|
||||
else:
|
||||
_drag_done()
|
||||
if _drag_cancelled:
|
||||
_show_text_edit()
|
||||
_drag_cancelled = true
|
||||
_is_editing = mb.pressed
|
||||
queue_redraw()
|
||||
|
||||
var mm := event as InputEventMouseMotion
|
||||
if mm and mm.button_mask & MOUSE_BUTTON_MASK_LEFT:
|
||||
_drag_motion(mm)
|
||||
_drag_cancelled = false
|
||||
|
||||
|
||||
func _notification(what: int) -> void:
|
||||
match what:
|
||||
NOTIFICATION_ENTER_TREE, NOTIFICATION_THEME_CHANGED:
|
||||
_update_stylebox()
|
||||
|
||||
NOTIFICATION_MOUSE_ENTER:
|
||||
_mouse_hovering = true
|
||||
queue_redraw()
|
||||
|
||||
NOTIFICATION_MOUSE_EXIT:
|
||||
_mouse_hovering = false
|
||||
queue_redraw()
|
||||
|
||||
NOTIFICATION_FOCUS_ENTER:
|
||||
if (Input.is_action_pressed("ui_focus_next") or Input.is_action_pressed("ui_focus_prev")) and not _line_edit_just_closed:
|
||||
_show_text_edit()
|
||||
_line_edit_just_closed = false
|
||||
|
||||
|
||||
func set_value(v: float) -> void:
|
||||
if is_equal_approx(v, value):
|
||||
return
|
||||
value = v
|
||||
emit_signal("value_changed", value)
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func _update_stylebox() -> void:
|
||||
_stylebox_normal = get_theme_stylebox("normal", "LineEdit")
|
||||
_stylebox_hover = StyleBoxFlat.new()
|
||||
_stylebox_hover.bg_color = get_theme_color("highlight_color", "Editor")
|
||||
_stylebox_editing = StyleBoxFlat.new()
|
||||
_stylebox_editing.bg_color = get_theme_color("dark_color_2", "Editor")
|
||||
_stylebox_value = StyleBoxFlat.new()
|
||||
_stylebox_value.bg_color = get_theme_color("accent_color", "Editor") * Color(1, 1, 1, 0.4)
|
||||
|
||||
|
||||
func _drag_prepare(mouse: InputEventMouse) -> void:
|
||||
_drag_dist = 0.0
|
||||
_drag_start_factor = (value - min_value) / (max_value - min_value)
|
||||
_drag_start_position = mouse.global_position
|
||||
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
|
||||
|
||||
|
||||
func _drag_done() -> void:
|
||||
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
||||
|
||||
if _drag_cancelled:
|
||||
Input.warp_mouse(_drag_start_position)
|
||||
else:
|
||||
Input.warp_mouse(global_position + size * Vector2(
|
||||
(value - min_value) / (max_value - min_value),
|
||||
0.5
|
||||
))
|
||||
value_submitted.emit()
|
||||
|
||||
|
||||
func _drag_motion(motion: InputEventMouseMotion) -> void:
|
||||
_drag_dist += motion.relative.x
|
||||
|
||||
var factor := _drag_start_factor + _drag_dist / size.x
|
||||
if factor < 0 or 1 < factor:
|
||||
factor = clamp(factor, 0, 1)
|
||||
_drag_dist = (factor - _drag_start_factor) * size.x
|
||||
|
||||
var v := factor * (max_value - min_value) + min_value
|
||||
var snap := motion.is_command_or_control_pressed() or motion.shift_pressed
|
||||
if snap and not (is_equal_approx(v, min_value) or is_equal_approx(v, max_value)):
|
||||
if motion.shift_pressed and motion.is_command_or_control_pressed():
|
||||
v = round(v * 1000.0) * 0.001
|
||||
elif motion.shift_pressed:
|
||||
v = round(v * 100.0) * 0.01
|
||||
else:
|
||||
v = round(v * 10.0) * 0.1
|
||||
|
||||
set_value(clamp(v, min_value, max_value))
|
||||
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func _show_text_edit() -> void:
|
||||
var gr := get_global_rect()
|
||||
_is_cancel = false
|
||||
_line_edit.text = str(value)
|
||||
_line_edit.set_position(gr.position)
|
||||
_line_edit.set_size(gr.size)
|
||||
_line_edit.show() # FIXME: no suitable solution for `show_modal` yet
|
||||
_line_edit.select_all()
|
||||
_line_edit.grab_focus()
|
||||
_line_edit.focus_next = find_next_valid_focus().get_path()
|
||||
_line_edit.focus_previous = find_prev_valid_focus().get_path()
|
||||
|
||||
|
||||
func _on_line_edit_focus_exited():
|
||||
if _line_edit.get_menu().visible:
|
||||
return
|
||||
if not _is_cancel and _line_edit.text.is_valid_float():
|
||||
set_value(clamp(_line_edit.text.to_float(), min_value, max_value))
|
||||
if not _line_edit_just_closed:
|
||||
_line_edit.hide()
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func _on_line_edit_text_entered(_text: String):
|
||||
_line_edit.hide()
|
||||
value_submitted.emit()
|
||||
|
||||
|
||||
func _on_line_edit_visibility_changed():
|
||||
if not _line_edit.visible:
|
||||
_line_edit_just_closed = true
|
||||
|
||||
|
||||
func _on_line_edit_gui_input(event: InputEvent):
|
||||
if event.is_action_pressed("ui_cancel"):
|
||||
_is_cancel = true
|
||||
_line_edit.hide()
|
||||
1
addons/gdfxr/editor/EditSlider.gd.uid
Normal file
1
addons/gdfxr/editor/EditSlider.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bqeewmmq5kpwx
|
||||
402
addons/gdfxr/editor/Editor.gd
Normal file
402
addons/gdfxr/editor/Editor.gd
Normal file
@@ -0,0 +1,402 @@
|
||||
@tool
|
||||
extends Container
|
||||
|
||||
enum ExtraOption { SAVE_AS, COPY, PASTE, PASTE_JSFXR, RECENT }
|
||||
enum DefaultFilename { EMPTY, GUESS_FOR_SAVE }
|
||||
|
||||
const SFXRConfig := preload("../SFXRConfig.gd")
|
||||
const SFXRGenerator := preload("../SFXRGenerator.gd")
|
||||
const Base58 := preload("../Base58.gd")
|
||||
const NUM_RECENTS := 4
|
||||
|
||||
class RecentEntry:
|
||||
var title: String
|
||||
var config := SFXRConfig.new()
|
||||
|
||||
var plugin: EditorPlugin:
|
||||
set(v):
|
||||
plugin = v
|
||||
for child in get_children():
|
||||
_hook_plugin(child)
|
||||
|
||||
var _config := SFXRConfig.new()
|
||||
var _config_defaults := SFXRConfig.new()
|
||||
var _config_clipboard: SFXRConfig
|
||||
var _config_recents: Array
|
||||
var _recents_id := 0
|
||||
var _generator := SFXRGenerator.new()
|
||||
var _path: String
|
||||
var _modified := false
|
||||
var _param_map := {}
|
||||
var _syncing_ui := false # a hack since Range set_value emits value_changed
|
||||
var _category_names := {}
|
||||
|
||||
@onready var audio_player := $AudioStreamPlayer as AudioStreamPlayer
|
||||
@onready var filename_label := find_child("Filename") as Label
|
||||
@onready var save_button := find_child("Save") as Button
|
||||
@onready var restore_button := find_child("Restore") as Button
|
||||
@onready var extra_button := find_child("Extra") as MenuButton
|
||||
@onready var version_button := find_child("VersionButton")
|
||||
@onready var translator := $PluginTranslator
|
||||
|
||||
|
||||
func _ready():
|
||||
if not plugin:
|
||||
return # Running in the edited scene instead of from Plugin
|
||||
|
||||
var popup := extra_button.get_popup()
|
||||
popup.add_item(translator.t("Save As..."), ExtraOption.SAVE_AS)
|
||||
popup.add_separator()
|
||||
popup.add_icon_item(get_theme_icon("ActionCopy", "EditorIcons"), translator.t("Copy"), ExtraOption.COPY)
|
||||
popup.add_icon_item(get_theme_icon("ActionPaste", "EditorIcons"), translator.t("Paste"), ExtraOption.PASTE)
|
||||
popup.add_item(translator.t("Paste from jsfxr"), ExtraOption.PASTE_JSFXR)
|
||||
popup.add_separator(translator.t("Recently Generated"))
|
||||
popup.id_pressed.connect(_on_Extra_id_pressed)
|
||||
|
||||
_category_names = {
|
||||
SFXRConfig.Category.PICKUP_COIN: translator.t("Pickup/Coin"),
|
||||
SFXRConfig.Category.LASER_SHOOT: translator.t("Laser/Shoot"),
|
||||
SFXRConfig.Category.EXPLOSION: translator.t("Explosion"),
|
||||
SFXRConfig.Category.POWERUP: translator.t("Powerup"),
|
||||
SFXRConfig.Category.HIT_HURT: translator.t("Hit/Hurt"),
|
||||
SFXRConfig.Category.JUMP: translator.t("Jump"),
|
||||
SFXRConfig.Category.BLIP_SELECT: translator.t("Blip/Select"),
|
||||
}
|
||||
|
||||
var params := find_child("Params") as Container
|
||||
for category in params.get_children():
|
||||
for control in category.get_children():
|
||||
_param_map[control.parameter] = control
|
||||
control.param_changed.connect(_on_param_changed)
|
||||
control.param_reset.connect(_on_param_reset)
|
||||
control.param_submitted.connect(_on_param_submitted)
|
||||
|
||||
_set_editing_file("")
|
||||
|
||||
|
||||
func _notification(what: int):
|
||||
if not plugin:
|
||||
return # Running in the edited scene instead of from Plugin
|
||||
|
||||
match what:
|
||||
NOTIFICATION_ENTER_TREE, NOTIFICATION_THEME_CHANGED:
|
||||
find_child("ScrollContainer").add_theme_stylebox_override("panel", get_theme_stylebox("panel", "Tree"))
|
||||
|
||||
if extra_button:
|
||||
var popup = extra_button.get_popup()
|
||||
popup.set_item_icon(popup.get_item_index(ExtraOption.COPY), get_theme_icon("ActionCopy", "EditorIcons"))
|
||||
popup.set_item_icon(popup.get_item_index(ExtraOption.PASTE), get_theme_icon("ActionPaste", "EditorIcons"))
|
||||
|
||||
|
||||
func edit(object: Variant) -> void:
|
||||
if not object:
|
||||
if not _modified:
|
||||
_set_editing_file("")
|
||||
# TODO: prompt?
|
||||
return
|
||||
|
||||
var path := object.resource_path as String
|
||||
if _modified:
|
||||
_popup_confirm(
|
||||
translator.t("There are unsaved changes.\nOpen '%s' anyway?") % path,
|
||||
_set_editing_file.bind(path)
|
||||
)
|
||||
else:
|
||||
_set_editing_file(path)
|
||||
|
||||
|
||||
func _hook_plugin(node: Node) -> void:
|
||||
if "plugin" in node:
|
||||
node.plugin = plugin
|
||||
for child in node.get_children():
|
||||
_hook_plugin(child)
|
||||
|
||||
|
||||
func _push_recent(title: String) -> void:
|
||||
var recent: RecentEntry
|
||||
if _config_recents.size() < NUM_RECENTS:
|
||||
recent = RecentEntry.new()
|
||||
else:
|
||||
recent = _config_recents.pop_back()
|
||||
|
||||
_recents_id += 1
|
||||
recent.title = "#%d %s" % [_recents_id, title]
|
||||
recent.config.copy_from(_config)
|
||||
|
||||
_config_recents.push_front(recent)
|
||||
|
||||
|
||||
func _popup_confirm(content: String, callback: Callable) -> void:
|
||||
var dialog := ConfirmationDialog.new()
|
||||
add_child(dialog)
|
||||
dialog.dialog_text = content
|
||||
dialog.title = translator.t("SFXR Editor")
|
||||
dialog.confirmed.connect(callback)
|
||||
dialog.popup_centered()
|
||||
dialog.visibility_changed.connect(dialog.queue_free)
|
||||
|
||||
|
||||
func _popup_message(content: String) -> void:
|
||||
var dialog := AcceptDialog.new()
|
||||
add_child(dialog)
|
||||
dialog.dialog_text = content
|
||||
dialog.title = translator.t("SFXR Editor")
|
||||
dialog.popup_centered()
|
||||
dialog.visibility_changed.connect(dialog.queue_free)
|
||||
|
||||
|
||||
func _popup_file_dialog(mode: int, callback: Callable, default_filename := DefaultFilename.EMPTY) -> void:
|
||||
var dialog := EditorFileDialog.new()
|
||||
add_child(dialog)
|
||||
dialog.access = EditorFileDialog.ACCESS_RESOURCES
|
||||
dialog.file_mode = mode
|
||||
|
||||
match default_filename:
|
||||
DefaultFilename.EMPTY:
|
||||
pass
|
||||
|
||||
DefaultFilename.GUESS_FOR_SAVE:
|
||||
if _path:
|
||||
dialog.current_path = _generate_serial_path(_path)
|
||||
|
||||
dialog.add_filter("*.sfxr; %s" % translator.t("SFXR Audio"))
|
||||
dialog.file_selected.connect(callback)
|
||||
dialog.popup_centered_ratio()
|
||||
dialog.visibility_changed.connect(dialog.queue_free)
|
||||
|
||||
|
||||
func _reset_defaults() -> void:
|
||||
_config_defaults.copy_from(_config)
|
||||
_set_modified(false)
|
||||
_sync_ui()
|
||||
|
||||
|
||||
func _restore_from_config(config: SFXRConfig) -> void:
|
||||
_config.copy_from(config)
|
||||
_sync_ui()
|
||||
_set_modified(not config.is_equal(_config_defaults))
|
||||
audio_player.stream = null
|
||||
|
||||
|
||||
func _set_editing_file(path: String) -> int: # Error
|
||||
if path.is_empty():
|
||||
_config.reset()
|
||||
audio_player.stream = null
|
||||
else:
|
||||
var err := _config.load(path)
|
||||
if err != OK:
|
||||
_popup_message(translator.t("'%s' is not a valid SFXR file.") % path)
|
||||
return err
|
||||
audio_player.stream = load(path)
|
||||
|
||||
_path = path
|
||||
_reset_defaults()
|
||||
return OK
|
||||
|
||||
|
||||
func _set_modified(value: bool) -> void:
|
||||
_modified = value
|
||||
|
||||
var has_file := not _path.is_empty()
|
||||
var base = _path if has_file else translator.t("Unsaved sound")
|
||||
if _modified:
|
||||
base += "(*)"
|
||||
filename_label.text = base
|
||||
restore_button.disabled = not _modified
|
||||
save_button.disabled = has_file and not _modified
|
||||
|
||||
|
||||
func _sync_ui() -> void:
|
||||
_syncing_ui = true
|
||||
for name in _param_map:
|
||||
var control = _param_map[name]
|
||||
var value = _config.get(name)
|
||||
control.set_value(value)
|
||||
control.set_resetable(value != _config_defaults.get(name))
|
||||
_syncing_ui = false
|
||||
|
||||
|
||||
func _generate_serial_path(path: String) -> String:
|
||||
var directory := DirAccess.open(path.get_base_dir())
|
||||
if not directory:
|
||||
return path
|
||||
|
||||
if not directory.file_exists(path.get_file()):
|
||||
return path
|
||||
|
||||
var basename := path.get_basename()
|
||||
var extension := path.get_extension()
|
||||
|
||||
# Extract trailing number.
|
||||
var num_string: String
|
||||
for i in range(basename.length() - 1, -1, -1):
|
||||
var c: String = basename[i]
|
||||
if "0" <= c and c <= "9":
|
||||
num_string = c + num_string
|
||||
else:
|
||||
break
|
||||
var number := num_string.to_int() if num_string else 0
|
||||
var name_string: String = basename.substr(0, basename.length() - num_string.length())
|
||||
|
||||
while true:
|
||||
number += 1
|
||||
var attemp := "%s%d.%s" % [name_string, number, extension]
|
||||
if not directory.file_exists(attemp):
|
||||
return attemp
|
||||
|
||||
return path # Unreachable
|
||||
|
||||
|
||||
func _on_param_submitted(_name):
|
||||
_on_Play_pressed(true)
|
||||
|
||||
|
||||
func _on_param_changed(name, value):
|
||||
if _syncing_ui:
|
||||
return
|
||||
|
||||
_config.set(name, value)
|
||||
|
||||
_param_map[name].set_resetable(value != _config_defaults.get(name))
|
||||
|
||||
_set_modified(not _config.is_equal(_config_defaults))
|
||||
audio_player.stream = null
|
||||
|
||||
|
||||
func _on_param_reset(name):
|
||||
var value = _config_defaults.get(name)
|
||||
_config.set(name, value)
|
||||
|
||||
_syncing_ui = true
|
||||
var control = _param_map[name]
|
||||
control.set_value(value)
|
||||
control.set_resetable(false)
|
||||
_syncing_ui = false
|
||||
|
||||
_set_modified(not _config.is_equal(_config_defaults))
|
||||
audio_player.stream = null
|
||||
_on_Play_pressed(true)
|
||||
|
||||
|
||||
func _on_Play_pressed(force_regenerate := false):
|
||||
if force_regenerate or audio_player.stream == null:
|
||||
audio_player.stream = _generator.generate_audio_stream(_config)
|
||||
audio_player.play()
|
||||
|
||||
|
||||
func _on_Randomize_pressed(category: int):
|
||||
if category == -1:
|
||||
_config.randomize()
|
||||
_push_recent(translator.t("Randomize"))
|
||||
else:
|
||||
_config.randomize_in_category(category)
|
||||
_push_recent(_category_names.get(category, "Unknown"))
|
||||
|
||||
_set_modified(true)
|
||||
_sync_ui()
|
||||
_on_Play_pressed(true)
|
||||
|
||||
|
||||
func _on_Mutate_pressed():
|
||||
_config.mutate()
|
||||
|
||||
_push_recent(translator.t("Mutate"))
|
||||
_set_modified(true)
|
||||
_sync_ui()
|
||||
_on_Play_pressed(true)
|
||||
|
||||
|
||||
func _on_Restore_pressed():
|
||||
_set_editing_file(_path)
|
||||
_on_Play_pressed(true)
|
||||
|
||||
|
||||
func _on_New_pressed():
|
||||
if _modified:
|
||||
_popup_confirm(
|
||||
translator.t("There are unsaved changes.\nCreate a new one anyway?"),
|
||||
_on_New_confirmed
|
||||
)
|
||||
else:
|
||||
_on_New_confirmed()
|
||||
|
||||
|
||||
func _on_New_confirmed() -> void:
|
||||
_set_editing_file("")
|
||||
|
||||
|
||||
func _on_Save_pressed():
|
||||
if _path.is_empty():
|
||||
_popup_file_dialog(EditorFileDialog.FILE_MODE_SAVE_FILE, _on_SaveAsDialog_confirmed)
|
||||
else:
|
||||
_config.save(_path)
|
||||
plugin.get_editor_interface().get_resource_filesystem().scan_sources()
|
||||
_reset_defaults()
|
||||
|
||||
|
||||
func _on_SaveAsDialog_confirmed(path: String):
|
||||
_path = path
|
||||
_config.save(path)
|
||||
plugin.get_editor_interface().get_resource_filesystem().scan()
|
||||
_reset_defaults()
|
||||
|
||||
|
||||
func _on_Load_pressed():
|
||||
if _modified:
|
||||
_popup_confirm(
|
||||
translator.t("There are unsaved changes.\nLoad anyway?"),
|
||||
_popup_file_dialog.bind(EditorFileDialog.FILE_MODE_OPEN_FILE, _set_editing_file)
|
||||
)
|
||||
else:
|
||||
_popup_file_dialog(EditorFileDialog.FILE_MODE_OPEN_FILE, _set_editing_file)
|
||||
|
||||
|
||||
func _on_Extra_about_to_show():
|
||||
var popup := extra_button.get_popup()
|
||||
popup.set_item_disabled(popup.get_item_index(ExtraOption.PASTE), _config_clipboard == null)
|
||||
popup.set_item_disabled(popup.get_item_index(ExtraOption.PASTE_JSFXR), not DisplayServer.clipboard_has())
|
||||
|
||||
# Rebuild recents menu everytime :)
|
||||
var first_recent_index := popup.get_item_index(ExtraOption.RECENT)
|
||||
if first_recent_index != -1:
|
||||
var count := popup.get_item_count()
|
||||
for i in count - first_recent_index:
|
||||
popup.remove_item(count - 1 - i)
|
||||
|
||||
if _config_recents.is_empty():
|
||||
popup.add_item(translator.t("None"), ExtraOption.RECENT)
|
||||
popup.set_item_disabled(popup.get_item_index(ExtraOption.RECENT), true)
|
||||
else:
|
||||
for i in _config_recents.size():
|
||||
popup.add_item(_config_recents[i].title, ExtraOption.RECENT + i)
|
||||
|
||||
|
||||
func _on_Extra_id_pressed(id: int) -> void:
|
||||
match id:
|
||||
ExtraOption.SAVE_AS:
|
||||
_popup_file_dialog(EditorFileDialog.FILE_MODE_SAVE_FILE, _on_SaveAsDialog_confirmed, DefaultFilename.GUESS_FOR_SAVE)
|
||||
|
||||
ExtraOption.COPY:
|
||||
if not _config_clipboard:
|
||||
_config_clipboard = SFXRConfig.new()
|
||||
_config_clipboard.copy_from(_config)
|
||||
|
||||
ExtraOption.PASTE:
|
||||
_restore_from_config(_config_clipboard)
|
||||
|
||||
ExtraOption.PASTE_JSFXR:
|
||||
var pasted := SFXRConfig.new()
|
||||
if pasted.load_from_base58(DisplayServer.clipboard_get()) == OK:
|
||||
_restore_from_config(pasted)
|
||||
else:
|
||||
_popup_message(translator.t("Clipboard does not contain code copied from jsfxr."))
|
||||
|
||||
_:
|
||||
var i := id - ExtraOption.RECENT as int
|
||||
if i < 0 or _config_recents.size() <= i:
|
||||
printerr("Bad index %d (%d in total)" % [i, _config_recents.size()])
|
||||
return
|
||||
var recent: RecentEntry = _config_recents[i]
|
||||
_restore_from_config(recent.config)
|
||||
_on_Play_pressed()
|
||||
|
||||
1
addons/gdfxr/editor/Editor.gd.uid
Normal file
1
addons/gdfxr/editor/Editor.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://b15etbgnf85gd
|
||||
297
addons/gdfxr/editor/Editor.tscn
Normal file
297
addons/gdfxr/editor/Editor.tscn
Normal file
@@ -0,0 +1,297 @@
|
||||
[gd_scene load_steps=7 format=3 uid="uid://dv8q33r8aan5b"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/gdfxr/editor/EditorIconButton.gd" id="1"]
|
||||
[ext_resource type="Script" path="res://addons/gdfxr/editor/Editor.gd" id="2"]
|
||||
[ext_resource type="PackedScene" uid="uid://p27e3x3kk5e0" path="res://addons/gdfxr/editor/ParamSlider.tscn" id="3"]
|
||||
[ext_resource type="PackedScene" uid="uid://b8wt6fq8w6mxc" path="res://addons/gdfxr/editor/ParamOption.tscn" id="4"]
|
||||
[ext_resource type="PackedScene" uid="uid://cewvefxbttrds" path="res://addons/gdfxr/editor/PluginTranslator.tscn" id="5"]
|
||||
[ext_resource type="PackedScene" path="res://addons/gdfxr/editor/VersionButton.tscn" id="6"]
|
||||
|
||||
[node name="Editor" type="VBoxContainer"]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("2")
|
||||
|
||||
[node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."]
|
||||
|
||||
[node name="Toolbar" type="HBoxContainer" parent="."]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="New" type="Button" parent="Toolbar"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "New"
|
||||
flat = true
|
||||
script = ExtResource("1")
|
||||
icon_name = "New"
|
||||
|
||||
[node name="Load" type="Button" parent="Toolbar"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Load"
|
||||
flat = true
|
||||
script = ExtResource("1")
|
||||
icon_name = "Load"
|
||||
|
||||
[node name="Save" type="Button" parent="Toolbar"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Save"
|
||||
flat = true
|
||||
script = ExtResource("1")
|
||||
icon_name = "Save"
|
||||
|
||||
[node name="Extra" type="MenuButton" parent="Toolbar"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Extra Options"
|
||||
script = ExtResource("1")
|
||||
icon_name = "GuiTabMenuHl"
|
||||
|
||||
[node name="VSeparator" type="VSeparator" parent="Toolbar"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Play" type="Button" parent="Toolbar"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
text = "Play"
|
||||
script = ExtResource("1")
|
||||
icon_name = "Play"
|
||||
|
||||
[node name="Restore" type="Button" parent="Toolbar"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Restore"
|
||||
disabled = true
|
||||
text = "Restore"
|
||||
script = ExtResource("1")
|
||||
icon_name = "Reload"
|
||||
|
||||
[node name="VSeparator2" type="VSeparator" parent="Toolbar"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Filename" type="Label" parent="Toolbar"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Unsaved sound"
|
||||
clip_text = true
|
||||
|
||||
[node name="VersionButton" parent="Toolbar" instance=ExtResource("6")]
|
||||
layout_mode = 2
|
||||
website = "https://github.com/timothyqiu/gdfxr"
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="."]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Editor" type="HBoxContainer" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Generators" type="VBoxContainer" parent="Editor"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Button" type="Button" parent="Editor/Generators"]
|
||||
layout_mode = 2
|
||||
text = "Pickup/Coin"
|
||||
|
||||
[node name="Button2" type="Button" parent="Editor/Generators"]
|
||||
layout_mode = 2
|
||||
text = "Laser/Shoot"
|
||||
|
||||
[node name="Button3" type="Button" parent="Editor/Generators"]
|
||||
layout_mode = 2
|
||||
text = "Explosion"
|
||||
|
||||
[node name="Button4" type="Button" parent="Editor/Generators"]
|
||||
layout_mode = 2
|
||||
text = "Powerup"
|
||||
|
||||
[node name="Button5" type="Button" parent="Editor/Generators"]
|
||||
layout_mode = 2
|
||||
text = "Hit/Hurt"
|
||||
|
||||
[node name="Button6" type="Button" parent="Editor/Generators"]
|
||||
layout_mode = 2
|
||||
text = "Jump"
|
||||
|
||||
[node name="Button7" type="Button" parent="Editor/Generators"]
|
||||
layout_mode = 2
|
||||
text = "Blip/Select"
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="Editor/Generators"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Button8" type="Button" parent="Editor/Generators"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Mutate"
|
||||
|
||||
[node name="Button9" type="Button" parent="Editor/Generators"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Randomize"
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="Editor"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="Params" type="HBoxContainer" parent="Editor/ScrollContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 6
|
||||
size_flags_vertical = 6
|
||||
|
||||
[node name="Envolope" type="VBoxContainer" parent="Editor/ScrollContainer/Params"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="ParamSlider" parent="Editor/ScrollContainer/Params/Envolope" instance=ExtResource("3")]
|
||||
layout_mode = 2
|
||||
label = "Attack Time"
|
||||
parameter = "p_env_attack"
|
||||
|
||||
[node name="ParamSlider2" parent="Editor/ScrollContainer/Params/Envolope" instance=ExtResource("3")]
|
||||
layout_mode = 2
|
||||
label = "Sustain Time"
|
||||
parameter = "p_env_sustain"
|
||||
|
||||
[node name="ParamSlider3" parent="Editor/ScrollContainer/Params/Envolope" instance=ExtResource("3")]
|
||||
layout_mode = 2
|
||||
label = "Sustain Punch"
|
||||
parameter = "p_env_punch"
|
||||
|
||||
[node name="ParamSlider4" parent="Editor/ScrollContainer/Params/Envolope" instance=ExtResource("3")]
|
||||
layout_mode = 2
|
||||
label = "Decay Time"
|
||||
parameter = "p_env_decay"
|
||||
|
||||
[node name="ParamSlider5" parent="Editor/ScrollContainer/Params/Envolope" instance=ExtResource("3")]
|
||||
layout_mode = 2
|
||||
label = "Change Amount"
|
||||
parameter = "p_arp_mod"
|
||||
bipolar = true
|
||||
|
||||
[node name="ParamSlider6" parent="Editor/ScrollContainer/Params/Envolope" instance=ExtResource("3")]
|
||||
layout_mode = 2
|
||||
label = "Change Speed"
|
||||
parameter = "p_arp_speed"
|
||||
|
||||
[node name="ParamSlider7" parent="Editor/ScrollContainer/Params/Envolope" instance=ExtResource("3")]
|
||||
layout_mode = 2
|
||||
label = "Volume"
|
||||
parameter = "sound_vol"
|
||||
|
||||
[node name="Frequency" type="VBoxContainer" parent="Editor/ScrollContainer/Params"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="ParamSlider" parent="Editor/ScrollContainer/Params/Frequency" instance=ExtResource("3")]
|
||||
layout_mode = 2
|
||||
label = "Start Frequency"
|
||||
parameter = "p_base_freq"
|
||||
|
||||
[node name="ParamSlider2" parent="Editor/ScrollContainer/Params/Frequency" instance=ExtResource("3")]
|
||||
layout_mode = 2
|
||||
label = "Min Frequency"
|
||||
parameter = "p_freq_limit"
|
||||
|
||||
[node name="ParamSlider3" parent="Editor/ScrollContainer/Params/Frequency" instance=ExtResource("3")]
|
||||
layout_mode = 2
|
||||
label = "Slide"
|
||||
parameter = "p_freq_ramp"
|
||||
bipolar = true
|
||||
|
||||
[node name="ParamSlider4" parent="Editor/ScrollContainer/Params/Frequency" instance=ExtResource("3")]
|
||||
layout_mode = 2
|
||||
label = "Delta Slide"
|
||||
parameter = "p_freq_dramp"
|
||||
bipolar = true
|
||||
|
||||
[node name="ParamSlider5" parent="Editor/ScrollContainer/Params/Frequency" instance=ExtResource("3")]
|
||||
layout_mode = 2
|
||||
label = "Vibrato Depth"
|
||||
parameter = "p_vib_strength"
|
||||
|
||||
[node name="ParamSlider6" parent="Editor/ScrollContainer/Params/Frequency" instance=ExtResource("3")]
|
||||
layout_mode = 2
|
||||
label = "Vibrato Speed"
|
||||
parameter = "p_vib_speed"
|
||||
|
||||
[node name="ParamSlider7" parent="Editor/ScrollContainer/Params/Frequency" instance=ExtResource("3")]
|
||||
layout_mode = 2
|
||||
label = "Repeat Speed"
|
||||
parameter = "p_repeat_speed"
|
||||
|
||||
[node name="Waveform" type="VBoxContainer" parent="Editor/ScrollContainer/Params"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="WaveformOption" parent="Editor/ScrollContainer/Params/Waveform" instance=ExtResource("4")]
|
||||
layout_mode = 2
|
||||
options = ["Square", "Sawtooth", "Sine", "Noise"]
|
||||
parameter = "wave_type"
|
||||
|
||||
[node name="ParamSlider" parent="Editor/ScrollContainer/Params/Waveform" instance=ExtResource("3")]
|
||||
layout_mode = 2
|
||||
label = "Square Duty"
|
||||
parameter = "p_duty"
|
||||
|
||||
[node name="ParamSlider2" parent="Editor/ScrollContainer/Params/Waveform" instance=ExtResource("3")]
|
||||
layout_mode = 2
|
||||
label = "Duty Sweep"
|
||||
parameter = "p_duty_ramp"
|
||||
bipolar = true
|
||||
|
||||
[node name="ParamSlider5" parent="Editor/ScrollContainer/Params/Waveform" instance=ExtResource("3")]
|
||||
layout_mode = 2
|
||||
label = "Phaser Offset"
|
||||
parameter = "p_pha_offset"
|
||||
bipolar = true
|
||||
|
||||
[node name="ParamSlider3" parent="Editor/ScrollContainer/Params/Waveform" instance=ExtResource("3")]
|
||||
layout_mode = 2
|
||||
label = "Phaser Sweep"
|
||||
parameter = "p_pha_ramp"
|
||||
bipolar = true
|
||||
|
||||
[node name="Filter" type="VBoxContainer" parent="Editor/ScrollContainer/Params"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="ParamSlider" parent="Editor/ScrollContainer/Params/Filter" instance=ExtResource("3")]
|
||||
layout_mode = 2
|
||||
label = "Low-pass Cutoff"
|
||||
parameter = "p_lpf_freq"
|
||||
|
||||
[node name="ParamSlider2" parent="Editor/ScrollContainer/Params/Filter" instance=ExtResource("3")]
|
||||
layout_mode = 2
|
||||
label = "Low-pass Sweep"
|
||||
parameter = "p_lpf_ramp"
|
||||
bipolar = true
|
||||
|
||||
[node name="ParamSlider5" parent="Editor/ScrollContainer/Params/Filter" instance=ExtResource("3")]
|
||||
layout_mode = 2
|
||||
label = "Low-pass Resonance"
|
||||
parameter = "p_lpf_resonance"
|
||||
|
||||
[node name="ParamSlider3" parent="Editor/ScrollContainer/Params/Filter" instance=ExtResource("3")]
|
||||
layout_mode = 2
|
||||
label = "High-pass Cutoff"
|
||||
parameter = "p_hpf_freq"
|
||||
|
||||
[node name="ParamSlider4" parent="Editor/ScrollContainer/Params/Filter" instance=ExtResource("3")]
|
||||
layout_mode = 2
|
||||
label = "High-pass Sweep"
|
||||
parameter = "p_hpf_ramp"
|
||||
bipolar = true
|
||||
|
||||
[node name="PluginTranslator" parent="." instance=ExtResource("5")]
|
||||
|
||||
[connection signal="pressed" from="Toolbar/New" to="." method="_on_New_pressed"]
|
||||
[connection signal="pressed" from="Toolbar/Load" to="." method="_on_Load_pressed"]
|
||||
[connection signal="pressed" from="Toolbar/Save" to="." method="_on_Save_pressed"]
|
||||
[connection signal="about_to_popup" from="Toolbar/Extra" to="." method="_on_Extra_about_to_show"]
|
||||
[connection signal="pressed" from="Toolbar/Play" to="." method="_on_Play_pressed"]
|
||||
[connection signal="pressed" from="Toolbar/Restore" to="." method="_on_Restore_pressed"]
|
||||
[connection signal="pressed" from="Editor/Generators/Button" to="." method="_on_Randomize_pressed" binds= [0]]
|
||||
[connection signal="pressed" from="Editor/Generators/Button2" to="." method="_on_Randomize_pressed" binds= [1]]
|
||||
[connection signal="pressed" from="Editor/Generators/Button3" to="." method="_on_Randomize_pressed" binds= [2]]
|
||||
[connection signal="pressed" from="Editor/Generators/Button4" to="." method="_on_Randomize_pressed" binds= [3]]
|
||||
[connection signal="pressed" from="Editor/Generators/Button5" to="." method="_on_Randomize_pressed" binds= [4]]
|
||||
[connection signal="pressed" from="Editor/Generators/Button6" to="." method="_on_Randomize_pressed" binds= [5]]
|
||||
[connection signal="pressed" from="Editor/Generators/Button7" to="." method="_on_Randomize_pressed" binds= [6]]
|
||||
[connection signal="pressed" from="Editor/Generators/Button8" to="." method="_on_Mutate_pressed"]
|
||||
[connection signal="pressed" from="Editor/Generators/Button9" to="." method="_on_Randomize_pressed" binds= [-1]]
|
||||
16
addons/gdfxr/editor/EditorIconButton.gd
Normal file
16
addons/gdfxr/editor/EditorIconButton.gd
Normal file
@@ -0,0 +1,16 @@
|
||||
@tool
|
||||
extends Button
|
||||
|
||||
@export var icon_name: String
|
||||
|
||||
var plugin: EditorPlugin # a hack to know if this is executing as plugin
|
||||
|
||||
|
||||
func _notification(what: int):
|
||||
if not plugin:
|
||||
return
|
||||
|
||||
match what:
|
||||
NOTIFICATION_ENTER_TREE, NOTIFICATION_THEME_CHANGED:
|
||||
if icon_name:
|
||||
icon = get_theme_icon(icon_name, "EditorIcons")
|
||||
1
addons/gdfxr/editor/EditorIconButton.gd.uid
Normal file
1
addons/gdfxr/editor/EditorIconButton.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://ch7ou8w11mw8r
|
||||
46
addons/gdfxr/editor/ParamOption.gd
Normal file
46
addons/gdfxr/editor/ParamOption.gd
Normal file
@@ -0,0 +1,46 @@
|
||||
@tool
|
||||
extends HBoxContainer
|
||||
|
||||
signal param_changed(name, value)
|
||||
signal param_submitted(name)
|
||||
signal param_reset(name)
|
||||
|
||||
@export var options: Array :
|
||||
set = set_options
|
||||
@export var parameter: String # Could be PackedStringArray, but pybabel won't catch that
|
||||
|
||||
@onready var option_button := $OptionButton as OptionButton
|
||||
|
||||
|
||||
func _ready():
|
||||
set_options(options)
|
||||
|
||||
|
||||
func set_options(v: Array) -> void:
|
||||
options = v
|
||||
|
||||
if is_inside_tree():
|
||||
option_button.clear()
|
||||
for item in options:
|
||||
option_button.add_item(item)
|
||||
|
||||
|
||||
func set_value(v: int) -> void:
|
||||
option_button.select(v)
|
||||
|
||||
|
||||
func get_value() -> int:
|
||||
return option_button.selected
|
||||
|
||||
|
||||
func set_resetable(v: bool) -> void:
|
||||
$Reset.disabled = not v
|
||||
|
||||
|
||||
func _on_OptionButton_item_selected(index: int):
|
||||
param_changed.emit(parameter, index)
|
||||
param_submitted.emit(parameter)
|
||||
|
||||
|
||||
func _on_Reset_pressed():
|
||||
param_reset.emit(parameter)
|
||||
1
addons/gdfxr/editor/ParamOption.gd.uid
Normal file
1
addons/gdfxr/editor/ParamOption.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://deaxh8vtv7bi
|
||||
35
addons/gdfxr/editor/ParamOption.tscn
Normal file
35
addons/gdfxr/editor/ParamOption.tscn
Normal file
@@ -0,0 +1,35 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://b8wt6fq8w6mxc"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/gdfxr/editor/ParamOption.gd" id="1"]
|
||||
[ext_resource type="Script" path="res://addons/gdfxr/editor/EditorIconButton.gd" id="2"]
|
||||
|
||||
[node name="WaveformOption" type="HBoxContainer"]
|
||||
offset_right = 253.0
|
||||
offset_bottom = 40.0
|
||||
pivot_offset = Vector2(41, -65)
|
||||
size_flags_horizontal = 3
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="Label" type="Label" parent="."]
|
||||
custom_minimum_size = Vector2(100, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Waveform"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="OptionButton" type="OptionButton" parent="."]
|
||||
custom_minimum_size = Vector2(105, 0)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
clip_text = true
|
||||
|
||||
[node name="Reset" type="Button" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
focus_mode = 0
|
||||
flat = true
|
||||
script = ExtResource("2")
|
||||
icon_name = "ReloadSmall"
|
||||
|
||||
[connection signal="item_selected" from="OptionButton" to="." method="_on_OptionButton_item_selected"]
|
||||
[connection signal="pressed" from="Reset" to="." method="_on_Reset_pressed"]
|
||||
60
addons/gdfxr/editor/ParamSlider.gd
Normal file
60
addons/gdfxr/editor/ParamSlider.gd
Normal file
@@ -0,0 +1,60 @@
|
||||
@tool
|
||||
extends HBoxContainer
|
||||
|
||||
signal param_changed(name, value)
|
||||
signal param_reset(name)
|
||||
signal param_submitted(name)
|
||||
|
||||
@export var label: String :
|
||||
set = set_label
|
||||
@export var parameter: String
|
||||
@export var bipolar := false :
|
||||
set = set_bipolar
|
||||
|
||||
|
||||
func _ready():
|
||||
set_label(label)
|
||||
set_bipolar(bipolar)
|
||||
|
||||
|
||||
func set_label(v: String) -> void:
|
||||
label = v
|
||||
if is_inside_tree():
|
||||
$Label.text = v
|
||||
|
||||
|
||||
func set_bipolar(v: bool) -> void:
|
||||
bipolar = v
|
||||
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
if bipolar:
|
||||
$HSlider.min_value = -1.0
|
||||
else:
|
||||
$HSlider.min_value = 0.0
|
||||
|
||||
|
||||
func set_value(v: float) -> void:
|
||||
$HSlider.value = v
|
||||
|
||||
|
||||
func get_value() -> float:
|
||||
return $HSlider.value
|
||||
|
||||
|
||||
func set_resetable(v: bool) -> void:
|
||||
$Reset.disabled = not v
|
||||
|
||||
|
||||
func _on_HSlider_value_changed(value: float):
|
||||
param_changed.emit(parameter, value)
|
||||
|
||||
|
||||
func _on_Reset_pressed():
|
||||
param_reset.emit(parameter)
|
||||
|
||||
|
||||
func _on_HSlider_value_submitted():
|
||||
param_submitted.emit(parameter)
|
||||
|
||||
1
addons/gdfxr/editor/ParamSlider.gd.uid
Normal file
1
addons/gdfxr/editor/ParamSlider.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://vkbrq7tdix7f
|
||||
37
addons/gdfxr/editor/ParamSlider.tscn
Normal file
37
addons/gdfxr/editor/ParamSlider.tscn
Normal file
@@ -0,0 +1,37 @@
|
||||
[gd_scene load_steps=4 format=3 uid="uid://p27e3x3kk5e0"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/gdfxr/editor/ParamSlider.gd" id="1"]
|
||||
[ext_resource type="Script" path="res://addons/gdfxr/editor/EditorIconButton.gd" id="2"]
|
||||
[ext_resource type="Script" path="res://addons/gdfxr/editor/EditSlider.gd" id="3"]
|
||||
|
||||
[node name="ParamSlider" type="HBoxContainer"]
|
||||
offset_right = 253.0
|
||||
offset_bottom = 40.0
|
||||
size_flags_horizontal = 3
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="Label" type="Label" parent="."]
|
||||
custom_minimum_size = Vector2(100, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="HSlider" type="Control" parent="."]
|
||||
clip_contents = true
|
||||
custom_minimum_size = Vector2(105, 0)
|
||||
layout_mode = 2
|
||||
focus_mode = 2
|
||||
mouse_default_cursor_shape = 10
|
||||
script = ExtResource("3")
|
||||
|
||||
[node name="Reset" type="Button" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
focus_mode = 0
|
||||
flat = true
|
||||
script = ExtResource("2")
|
||||
icon_name = "ReloadSmall"
|
||||
|
||||
[connection signal="value_submitted" from="HSlider" to="." method="_on_HSlider_value_submitted"]
|
||||
[connection signal="value_changed" from="HSlider" to="." method="_on_HSlider_value_changed"]
|
||||
[connection signal="pressed" from="Reset" to="." method="_on_Reset_pressed"]
|
||||
57
addons/gdfxr/editor/PluginTranslator.gd
Normal file
57
addons/gdfxr/editor/PluginTranslator.gd
Normal file
@@ -0,0 +1,57 @@
|
||||
@tool
|
||||
extends Node
|
||||
|
||||
var plugin: EditorPlugin :
|
||||
set = set_plugin
|
||||
|
||||
var _translation: Translation
|
||||
|
||||
|
||||
func set_plugin(v: EditorPlugin) -> void:
|
||||
if plugin == v:
|
||||
return
|
||||
if not v:
|
||||
plugin = null
|
||||
_translation = null
|
||||
return
|
||||
|
||||
plugin = v
|
||||
var locale: String = plugin.get_editor_interface().get_editor_settings().get('interface/editor/editor_language')
|
||||
var script := get_script() as Script
|
||||
var path := script.resource_path.get_base_dir().path_join("translations/%s.po" % locale)
|
||||
if ResourceLoader.exists(path):
|
||||
_translation = ResourceLoader.load(path)
|
||||
|
||||
if _translation:
|
||||
_translate_node.call_deferred(get_parent())
|
||||
|
||||
|
||||
func t(message: StringName) -> String:
|
||||
if _translation:
|
||||
var translated := _translation.get_message(message)
|
||||
if translated:
|
||||
return String(translated)
|
||||
return String(message)
|
||||
|
||||
|
||||
func _translate_node(node: Node):
|
||||
if node is Control:
|
||||
node.tooltip_text = t(node.tooltip_text)
|
||||
|
||||
if node is HBoxContainer and node.has_method("set_options"):
|
||||
var options = []
|
||||
for item in node.options:
|
||||
options.append(t(item))
|
||||
node.options = options
|
||||
|
||||
if node is Button and not node is OptionButton:
|
||||
node.text = t(node.text)
|
||||
|
||||
if node is Label:
|
||||
node.text = t(node.text)
|
||||
|
||||
if node is Slider:
|
||||
node.tooltip_text = t(node.tooltip_text)
|
||||
|
||||
for child in node.get_children():
|
||||
_translate_node(child)
|
||||
1
addons/gdfxr/editor/PluginTranslator.gd.uid
Normal file
1
addons/gdfxr/editor/PluginTranslator.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bifafveiktn3w
|
||||
6
addons/gdfxr/editor/PluginTranslator.tscn
Normal file
6
addons/gdfxr/editor/PluginTranslator.tscn
Normal file
@@ -0,0 +1,6 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://cewvefxbttrds"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/gdfxr/editor/PluginTranslator.gd" id="1"]
|
||||
|
||||
[node name="PluginTranslator" type="Node"]
|
||||
script = ExtResource("1")
|
||||
26
addons/gdfxr/editor/VersionButton.gd
Normal file
26
addons/gdfxr/editor/VersionButton.gd
Normal file
@@ -0,0 +1,26 @@
|
||||
@tool
|
||||
extends LinkButton
|
||||
|
||||
@export var website: String
|
||||
|
||||
var plugin: EditorPlugin :
|
||||
set = set_plugin
|
||||
|
||||
|
||||
func set_plugin(v: EditorPlugin) -> void:
|
||||
plugin = v
|
||||
|
||||
var script := get_script() as Script
|
||||
var path := script.resource_path.get_base_dir().path_join("../plugin.cfg")
|
||||
|
||||
var cfg := ConfigFile.new()
|
||||
var err := cfg.load(path)
|
||||
text = "%s v%s" % [
|
||||
cfg.get_value("plugin", "name", "plugin"),
|
||||
cfg.get_value("plugin", "version", "1.0"),
|
||||
]
|
||||
|
||||
|
||||
func _on_VersionButton_pressed():
|
||||
if website:
|
||||
OS.shell_open(website)
|
||||
1
addons/gdfxr/editor/VersionButton.gd.uid
Normal file
1
addons/gdfxr/editor/VersionButton.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cb3ifjg0krnam
|
||||
19
addons/gdfxr/editor/VersionButton.tscn
Normal file
19
addons/gdfxr/editor/VersionButton.tscn
Normal file
@@ -0,0 +1,19 @@
|
||||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://addons/gdfxr/editor/VersionButton.gd" type="Script" id=1]
|
||||
|
||||
[node name="VersionButton" type="LinkButton"]
|
||||
self_modulate = Color( 1, 1, 1, 0.65 )
|
||||
offset_left = 1024.0
|
||||
offset_top = 5.0
|
||||
offset_right = 1024.0
|
||||
offset_bottom = 19.0
|
||||
focus_mode = 2
|
||||
size_flags_vertical = 4
|
||||
underline = 1
|
||||
script = ExtResource( 1 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[connection signal="pressed" from="." to="." method="_on_VersionButton_pressed"]
|
||||
265
addons/gdfxr/editor/translations/zh_CN.po
Normal file
265
addons/gdfxr/editor/translations/zh_CN.po
Normal file
@@ -0,0 +1,265 @@
|
||||
# Translations template for gdfxr.
|
||||
# Copyright (C) 2022 Haoyu Qiu
|
||||
# This file is distributed under the same license as the gdfxr project.
|
||||
# Haoyu Qiu <timothyqiu32@gmail.com>, 2022.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: gdfxr 1.0\n"
|
||||
"Report-Msgid-Bugs-To: timothyqiu32@gmail.com\n"
|
||||
"POT-Creation-Date: 2022-12-04 13:45+0800\n"
|
||||
"PO-Revision-Date: 2022-12-04 13:45+0800\n"
|
||||
"Last-Translator: Haoyu Qiu <timothyqiu32@gmail.com>\n"
|
||||
"Language-Team: \n"
|
||||
"Language: zh_CN\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"Generated-By: Babel 2.9.1\n"
|
||||
"X-Generator: Poedit 3.2.1\n"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.gd
|
||||
msgid "Save As..."
|
||||
msgstr "另存为..."
|
||||
|
||||
#: addons/gdfxr/editor/Editor.gd
|
||||
msgid "Copy"
|
||||
msgstr "复制"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.gd
|
||||
msgid "Paste"
|
||||
msgstr "粘贴"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.gd
|
||||
msgid "Paste from jsfxr"
|
||||
msgstr "从 jsfxr 粘贴"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.gd
|
||||
msgid "Recently Generated"
|
||||
msgstr "最近生成"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.gd addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Pickup/Coin"
|
||||
msgstr "拾取/金币"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.gd addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Laser/Shoot"
|
||||
msgstr "激光/射击"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.gd addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Explosion"
|
||||
msgstr "爆炸"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.gd addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Powerup"
|
||||
msgstr "升级"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.gd addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Hit/Hurt"
|
||||
msgstr "击中/受伤"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.gd addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Jump"
|
||||
msgstr "跳跃"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.gd addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Blip/Select"
|
||||
msgstr "短滴/选择"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.gd
|
||||
#, python-format
|
||||
msgid ""
|
||||
"There are unsaved changes.\n"
|
||||
"Open '%s' anyway?"
|
||||
msgstr ""
|
||||
"存在未保存的修改。\n"
|
||||
"仍然要打开“%s”吗?"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.gd
|
||||
msgid "SFXR Editor"
|
||||
msgstr "SFXR 编辑器"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.gd
|
||||
msgid "SFXR Audio"
|
||||
msgstr "SFXR 音频"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.gd
|
||||
#, python-format
|
||||
msgid "'%s' is not a valid SFXR file."
|
||||
msgstr "“%s”不是有效的 SFXR 文件。"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.gd addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Unsaved sound"
|
||||
msgstr "未保存音效"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.gd addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Randomize"
|
||||
msgstr "随机"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.gd addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Mutate"
|
||||
msgstr "演化"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.gd
|
||||
msgid ""
|
||||
"There are unsaved changes.\n"
|
||||
"Create a new one anyway?"
|
||||
msgstr ""
|
||||
"存在未保存的修改。\n"
|
||||
"仍然要新建吗?"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.gd
|
||||
msgid ""
|
||||
"There are unsaved changes.\n"
|
||||
"Load anyway?"
|
||||
msgstr ""
|
||||
"存在未保存的修改。\n"
|
||||
"仍然要加载吗?"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.gd
|
||||
msgid "None"
|
||||
msgstr "无"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.gd
|
||||
msgid "Clipboard does not contain code copied from jsfxr."
|
||||
msgstr "剪贴板中没有从 jsfxr 复制的代码。"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "New"
|
||||
msgstr "新建"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Load"
|
||||
msgstr "加载"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Save"
|
||||
msgstr "保存"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Extra Options"
|
||||
msgstr "更多选项"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Play"
|
||||
msgstr "播放"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Restore"
|
||||
msgstr "恢复"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Attack Time"
|
||||
msgstr "起音时间"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Sustain Time"
|
||||
msgstr "延音时间"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Sustain Punch"
|
||||
msgstr "延音冲击"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Decay Time"
|
||||
msgstr "释音时间"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Change Amount"
|
||||
msgstr "改变强度"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Change Speed"
|
||||
msgstr "改变速度"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Volume"
|
||||
msgstr "音量"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Start Frequency"
|
||||
msgstr "起始频率"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Min Frequency"
|
||||
msgstr "最低频率"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Slide"
|
||||
msgstr "滑音"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Delta Slide"
|
||||
msgstr "滑音增量"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Vibrato Depth"
|
||||
msgstr "颤音深度"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Vibrato Speed"
|
||||
msgstr "颤音速度"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Repeat Speed"
|
||||
msgstr "重复速度"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Square"
|
||||
msgstr "方波"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Sawtooth"
|
||||
msgstr "锯齿波"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Sine"
|
||||
msgstr "正弦波"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Noise"
|
||||
msgstr "噪波"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Square Duty"
|
||||
msgstr "方波工作"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Duty Sweep"
|
||||
msgstr "工作变频"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Phaser Offset"
|
||||
msgstr "相位偏移"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Phaser Sweep"
|
||||
msgstr "相位变频"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Low-pass Cutoff"
|
||||
msgstr "低通截频"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Low-pass Sweep"
|
||||
msgstr "低通变频"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "Low-pass Resonance"
|
||||
msgstr "低通共振"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "High-pass Cutoff"
|
||||
msgstr "高通截频"
|
||||
|
||||
#: addons/gdfxr/editor/Editor.tscn
|
||||
msgid "High-pass Sweep"
|
||||
msgstr "高通变频"
|
||||
|
||||
#: addons/gdfxr/editor/ParamOption.tscn
|
||||
msgid "Waveform"
|
||||
msgstr "波形"
|
||||
|
||||
#~ msgid "Hold Ctrl to snap to 0.01 increments."
|
||||
#~ msgstr "按住 Ctrl 吸附到 0.01 增量。"
|
||||
Reference in New Issue
Block a user