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