196 lines
6.5 KiB
GDScript3
196 lines
6.5 KiB
GDScript3
|
|
extends PopupPanel
|
||
|
|
|
||
|
|
## Dynamic settings popup for a specific plugin.
|
||
|
|
## Touch-friendly: large close button, exclusive modal, ample control sizes.
|
||
|
|
|
||
|
|
const CLOSE_BTN_SIZE: Vector2 = Vector2(56, 56)
|
||
|
|
const WINDOW_CORNER_RADIUS: float = 12.0
|
||
|
|
|
||
|
|
var _plugin_id: String = ""
|
||
|
|
var _controls: Dictionary = {} # setting_key -> Control
|
||
|
|
|
||
|
|
|
||
|
|
func setup(plugin_id: String) -> void:
|
||
|
|
_plugin_id = plugin_id
|
||
|
|
exclusive = true
|
||
|
|
_build_ui()
|
||
|
|
|
||
|
|
|
||
|
|
func _build_ui() -> void:
|
||
|
|
# Window background with rounded corners
|
||
|
|
var bg := StyleBoxFlat.new()
|
||
|
|
bg.bg_color = Color(0.12, 0.12, 0.16, 1.0)
|
||
|
|
bg.corner_radius_top_left = WINDOW_CORNER_RADIUS
|
||
|
|
bg.corner_radius_top_right = WINDOW_CORNER_RADIUS
|
||
|
|
bg.corner_radius_bottom_left = WINDOW_CORNER_RADIUS
|
||
|
|
bg.corner_radius_bottom_right = WINDOW_CORNER_RADIUS
|
||
|
|
add_theme_stylebox_override("panel", bg)
|
||
|
|
|
||
|
|
# Content area — leave right margin for the overlaid close button
|
||
|
|
var mc := MarginContainer.new()
|
||
|
|
mc.add_theme_constant_override("margin_left", 16)
|
||
|
|
mc.add_theme_constant_override("margin_right", 16 + CLOSE_BTN_SIZE.x)
|
||
|
|
mc.add_theme_constant_override("margin_top", 12)
|
||
|
|
mc.add_theme_constant_override("margin_bottom", 12)
|
||
|
|
add_child(mc)
|
||
|
|
|
||
|
|
var vbox := VBoxContainer.new()
|
||
|
|
vbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
|
|
vbox.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||
|
|
mc.add_child(vbox)
|
||
|
|
|
||
|
|
# Title bar
|
||
|
|
var title_bar := HBoxContainer.new()
|
||
|
|
var title_label := Label.new()
|
||
|
|
var info: Dictionary = PluginManager.get_plugin(_plugin_id)
|
||
|
|
title_label.text = "%s Settings" % info.get("name", _plugin_id)
|
||
|
|
title_label.add_theme_font_size_override("font_size", 20)
|
||
|
|
title_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
|
|
title_bar.add_child(title_label)
|
||
|
|
vbox.add_child(title_bar)
|
||
|
|
|
||
|
|
vbox.add_child(HSeparator.new())
|
||
|
|
|
||
|
|
# Setting rows
|
||
|
|
var settings: Array[Dictionary] = PluginManager.get_plugin_settings(_plugin_id)
|
||
|
|
if settings.is_empty():
|
||
|
|
var empty := Label.new()
|
||
|
|
empty.text = "No configurable settings for this plugin."
|
||
|
|
empty.modulate = Color(0.5, 0.5, 0.55)
|
||
|
|
empty.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||
|
|
vbox.add_child(empty)
|
||
|
|
return
|
||
|
|
|
||
|
|
var inner := MarginContainer.new()
|
||
|
|
inner.add_theme_constant_override("margin_left", 4)
|
||
|
|
inner.add_theme_constant_override("margin_right", 4)
|
||
|
|
inner.add_theme_constant_override("margin_top", 10)
|
||
|
|
inner.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||
|
|
vbox.add_child(inner)
|
||
|
|
|
||
|
|
var scroll := ScrollContainer.new()
|
||
|
|
scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||
|
|
inner.add_child(scroll)
|
||
|
|
|
||
|
|
var form := VBoxContainer.new()
|
||
|
|
form.add_theme_constant_override("separation", 14)
|
||
|
|
form.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
|
|
scroll.add_child(form)
|
||
|
|
|
||
|
|
for sd in settings:
|
||
|
|
var key: String = sd.get("key", "")
|
||
|
|
if key.is_empty():
|
||
|
|
continue
|
||
|
|
var label_text: String = sd.get("label", key)
|
||
|
|
var stype: String = sd.get("type", "string")
|
||
|
|
var default_val: Variant = sd.get("default", null)
|
||
|
|
var current_val: Variant = PluginManager.get_plugin_setting(_plugin_id, key, default_val)
|
||
|
|
|
||
|
|
var row := HBoxContainer.new()
|
||
|
|
row.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
|
|
|
||
|
|
var lbl := Label.new()
|
||
|
|
lbl.text = label_text
|
||
|
|
lbl.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
|
|
lbl.mouse_filter = Control.MOUSE_FILTER_PASS
|
||
|
|
row.add_child(lbl)
|
||
|
|
|
||
|
|
var ctrl := _build_control(stype, key, current_val, sd)
|
||
|
|
if ctrl != null:
|
||
|
|
ctrl.custom_minimum_size = Vector2(180, 40)
|
||
|
|
_controls[key] = ctrl
|
||
|
|
row.add_child(ctrl)
|
||
|
|
|
||
|
|
form.add_child(row)
|
||
|
|
|
||
|
|
# Red corner close button (overlaid, anchored top-right, flush to edge)
|
||
|
|
var close_button := Button.new()
|
||
|
|
close_button.text = "✕"
|
||
|
|
close_button.add_theme_font_size_override("font_size", 22)
|
||
|
|
close_button.flat = true
|
||
|
|
close_button.pressed.connect(_close)
|
||
|
|
close_button.custom_minimum_size = CLOSE_BTN_SIZE
|
||
|
|
|
||
|
|
var btn_style := StyleBoxFlat.new()
|
||
|
|
btn_style.bg_color = Color(1.0, 0.0, 0.0, 1.0)
|
||
|
|
btn_style.corner_radius_top_right = WINDOW_CORNER_RADIUS
|
||
|
|
btn_style.corner_radius_bottom_left = WINDOW_CORNER_RADIUS
|
||
|
|
btn_style.corner_radius_top_left = 0
|
||
|
|
btn_style.corner_radius_bottom_right = 0
|
||
|
|
btn_style.set_corner_radius(Corner.CORNER_TOP_RIGHT, WINDOW_CORNER_RADIUS)
|
||
|
|
btn_style.set_corner_radius(Corner.CORNER_BOTTOM_LEFT, WINDOW_CORNER_RADIUS)
|
||
|
|
close_button.add_theme_stylebox_override("normal", btn_style)
|
||
|
|
|
||
|
|
var hover_style := StyleBoxFlat.new()
|
||
|
|
hover_style.bg_color = Color(0.85, 0.0, 0.0, 1.0)
|
||
|
|
hover_style.corner_radius_top_right = WINDOW_CORNER_RADIUS
|
||
|
|
hover_style.corner_radius_bottom_left = WINDOW_CORNER_RADIUS
|
||
|
|
hover_style.corner_radius_top_left = 0
|
||
|
|
hover_style.corner_radius_bottom_right = 0
|
||
|
|
close_button.add_theme_stylebox_override("hover", hover_style)
|
||
|
|
|
||
|
|
var pressed_style := StyleBoxFlat.new()
|
||
|
|
pressed_style.bg_color = Color(0.7, 0.0, 0.0, 1.0)
|
||
|
|
pressed_style.corner_radius_top_right = WINDOW_CORNER_RADIUS
|
||
|
|
pressed_style.corner_radius_bottom_left = WINDOW_CORNER_RADIUS
|
||
|
|
pressed_style.corner_radius_top_left = 0
|
||
|
|
pressed_style.corner_radius_bottom_right = 0
|
||
|
|
close_button.add_theme_stylebox_override("pressed", pressed_style)
|
||
|
|
|
||
|
|
close_button.set_anchors_preset(Control.PRESET_TOP_RIGHT)
|
||
|
|
add_child(close_button)
|
||
|
|
|
||
|
|
|
||
|
|
func _build_control(stype: String, key: String, val: Variant, defn: Dictionary) -> Control:
|
||
|
|
match stype:
|
||
|
|
"int":
|
||
|
|
var sb := SpinBox.new()
|
||
|
|
sb.min_value = defn.get("min", -999999)
|
||
|
|
sb.max_value = defn.get("max", 999999)
|
||
|
|
sb.step = 1
|
||
|
|
sb.value = int(val) if val != null else int(defn.get("default", 0))
|
||
|
|
sb.custom_minimum_size = Vector2(0, 40)
|
||
|
|
sb.value_changed.connect(_on_value_changed.bind(key))
|
||
|
|
return sb
|
||
|
|
|
||
|
|
"float":
|
||
|
|
var sb := SpinBox.new()
|
||
|
|
sb.min_value = defn.get("min", -999999.0)
|
||
|
|
sb.max_value = defn.get("max", 999999.0)
|
||
|
|
sb.step = defn.get("step", 0.1)
|
||
|
|
sb.value = float(val) if val != null else float(defn.get("default", 0.0))
|
||
|
|
sb.custom_minimum_size = Vector2(0, 40)
|
||
|
|
sb.value_changed.connect(_on_value_changed.bind(key))
|
||
|
|
return sb
|
||
|
|
|
||
|
|
"bool":
|
||
|
|
var cb := CheckBox.new()
|
||
|
|
cb.button_pressed = bool(val) if val != null else bool(defn.get("default", false))
|
||
|
|
cb.custom_minimum_size = Vector2(0, 40)
|
||
|
|
cb.toggled.connect(_on_toggled.bind(key))
|
||
|
|
return cb
|
||
|
|
|
||
|
|
_:
|
||
|
|
# string / default
|
||
|
|
var le := LineEdit.new()
|
||
|
|
le.text = str(val) if val != null else str(defn.get("default", ""))
|
||
|
|
le.custom_minimum_size = Vector2(0, 40)
|
||
|
|
le.text_changed.connect(_on_text_changed.bind(key))
|
||
|
|
return le
|
||
|
|
|
||
|
|
|
||
|
|
func _on_value_changed(value: float, key: String) -> void:
|
||
|
|
PluginManager.set_plugin_setting(_plugin_id, key, value)
|
||
|
|
|
||
|
|
|
||
|
|
func _on_toggled(checked: bool, key: String) -> void:
|
||
|
|
PluginManager.set_plugin_setting(_plugin_id, key, checked)
|
||
|
|
|
||
|
|
|
||
|
|
func _on_text_changed(text: String, key: String) -> void:
|
||
|
|
PluginManager.set_plugin_setting(_plugin_id, key, text)
|
||
|
|
|
||
|
|
|
||
|
|
func _close() -> void:
|
||
|
|
queue_free()
|