extends VBoxContainer const Brand := preload("res://scripts/ui/medmind_style.gd") const QUESTION_TEXT_SCALE_MIN := 0.85 const QUESTION_TEXT_SCALE_MAX := 1.35 const QUESTION_TEXT_SCALE_STEP := 0.1 const CORE_BLOCK_SIZE_STEP := 5 var main: Control var theme_toggle: CheckButton var sound_toggle: CheckButton var question_scale_value_label: Label var ccs_scale_value_label: Label var core_block_size_value_label: Label var return_screen := "" var back_to_session_button: Button func _ready() -> void: set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) add_theme_constant_override("separation", 18) var eyebrow := Label.new() eyebrow.text = "PREFERENCES" eyebrow.add_theme_font_override("font", Brand.font) eyebrow.add_theme_font_size_override("font_size", 14) eyebrow.add_theme_color_override("font_color", Brand.SKY_BLUE) add_child(eyebrow) var title := Label.new() title.text = "Settings" title.add_theme_font_override("font", Brand.font) title.add_theme_font_size_override("font_size", 34) add_child(title) var subtitle := Label.new() subtitle.text = "Adjust RanaZe's appearance and interface sounds." subtitle.add_theme_font_override("font", Brand.font) subtitle.add_theme_font_size_override("font_size", 17) subtitle.add_theme_color_override("font_color", Brand.SLATE) add_child(subtitle) back_to_session_button = Button.new() back_to_session_button.text = "Back to session" back_to_session_button.custom_minimum_size = Vector2(0, 44) back_to_session_button.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN back_to_session_button.add_theme_stylebox_override("normal", Brand.gradient_button_box()) back_to_session_button.add_theme_color_override("font_color", Color.WHITE) back_to_session_button.visible = false UISoundService.set_button_sound(back_to_session_button, "back") back_to_session_button.pressed.connect(_return_to_session) add_child(back_to_session_button) var panel := PanelContainer.new() panel.custom_minimum_size = Vector2(520, 0) panel.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN panel.add_theme_stylebox_override("panel", Brand.panel_box(UIThemeService.is_dark_mode())) add_child(panel) var content := VBoxContainer.new() content.add_theme_constant_override("separation", 18) panel.add_child(content) theme_toggle = CheckButton.new() theme_toggle.text = "Dark mode" theme_toggle.custom_minimum_size = Vector2(0, 52) theme_toggle.toggled.connect(_on_theme_toggled) content.add_child(theme_toggle) sound_toggle = CheckButton.new() sound_toggle.text = "Interface sounds" sound_toggle.custom_minimum_size = Vector2(0, 52) sound_toggle.toggled.connect(UISoundService.set_sounds_enabled) content.add_child(sound_toggle) content.add_child(_build_text_scale_row("Question bank text size", "question")) content.add_child(_build_text_scale_row("CCS case text size", "ccs")) content.add_child(_build_core_block_size_row()) var legal_panel := PanelContainer.new() legal_panel.custom_minimum_size = Vector2(520, 0) legal_panel.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN legal_panel.add_theme_stylebox_override("panel", Brand.panel_box(UIThemeService.is_dark_mode())) legal_panel.set_meta("legal_panel", true) add_child(legal_panel) var legal_content := VBoxContainer.new() legal_content.add_theme_constant_override("separation", 10) legal_panel.add_child(legal_content) var legal_heading := Label.new() legal_heading.text = "Legal" legal_heading.add_theme_font_override("font", Brand.font) legal_heading.add_theme_font_size_override("font_size", 20) legal_content.add_child(legal_heading) for spec in [["Terms of Service", "terms"], ["Privacy Policy", "privacy"]]: var button := Button.new() button.text = spec[0] button.alignment = HORIZONTAL_ALIGNMENT_LEFT button.pressed.connect(_open_legal_document.bind(str(spec[1]))) legal_content.add_child(button) UIThemeService.theme_changed.connect(_on_theme_changed) AccountProfileService.profile_changed.connect(_refresh_text_scale_labels) CCSService.text_scale_changed.connect(_refresh_text_scale_labels) _refresh_text_scale_labels() func on_show(payload: Variant = null) -> void: var wrapper: Dictionary = payload if typeof(payload) == TYPE_DICTIONARY else {} return_screen = str(wrapper.get("return_screen", "")) back_to_session_button.visible = not return_screen.is_empty() theme_toggle.set_pressed_no_signal(UIThemeService.is_dark_mode()) sound_toggle.set_pressed_no_signal(UISoundService.are_sounds_enabled()) _refresh_text_scale_labels() func _build_text_scale_row(label_text: String, kind: String) -> HBoxContainer: var row := HBoxContainer.new() row.custom_minimum_size = Vector2(0, 52) row.add_theme_constant_override("separation", 12) var label := Label.new() label.text = label_text label.size_flags_horizontal = Control.SIZE_EXPAND_FILL row.add_child(label) var smaller := Button.new() smaller.text = "A-" smaller.tooltip_text = "Decrease text size" smaller.custom_minimum_size = Vector2(44, 44) row.add_child(smaller) var value_label := Label.new() value_label.custom_minimum_size = Vector2(56, 0) value_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER row.add_child(value_label) var larger := Button.new() larger.text = "A+" larger.tooltip_text = "Increase text size" larger.custom_minimum_size = Vector2(44, 44) row.add_child(larger) if kind == "question": question_scale_value_label = value_label smaller.pressed.connect(_adjust_question_text_scale.bind(-QUESTION_TEXT_SCALE_STEP)) larger.pressed.connect(_adjust_question_text_scale.bind(QUESTION_TEXT_SCALE_STEP)) else: ccs_scale_value_label = value_label smaller.pressed.connect(_adjust_ccs_text_scale.bind(-CCSService.TEXT_SCALE_STEP)) larger.pressed.connect(_adjust_ccs_text_scale.bind(CCSService.TEXT_SCALE_STEP)) return row func _build_core_block_size_row() -> HBoxContainer: var row := HBoxContainer.new() row.custom_minimum_size = Vector2(0, 52) row.add_theme_constant_override("separation", 12) var label := Label.new() label.text = "Question block size" label.size_flags_horizontal = Control.SIZE_EXPAND_FILL row.add_child(label) var smaller := Button.new() smaller.text = "-" smaller.tooltip_text = "Fewer questions per block" smaller.custom_minimum_size = Vector2(44, 44) smaller.pressed.connect(_adjust_core_block_size.bind(-CORE_BLOCK_SIZE_STEP)) row.add_child(smaller) core_block_size_value_label = Label.new() core_block_size_value_label.custom_minimum_size = Vector2(56, 0) core_block_size_value_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER row.add_child(core_block_size_value_label) var larger := Button.new() larger.text = "+" larger.tooltip_text = "More questions per block" larger.custom_minimum_size = Vector2(44, 44) larger.pressed.connect(_adjust_core_block_size.bind(CORE_BLOCK_SIZE_STEP)) row.add_child(larger) return row func _adjust_core_block_size(delta: int) -> void: BlockSessionService.set_core_block_size(BlockSessionService.core_block_size + delta) _refresh_text_scale_labels() func _adjust_question_text_scale(delta: float) -> void: var current: float = AccountProfileService.preferences.get("questionTextScale", 1.0) var next_scale: float = clamp(current + delta, QUESTION_TEXT_SCALE_MIN, QUESTION_TEXT_SCALE_MAX) AccountProfileService.update_text_scale(next_scale) _refresh_text_scale_labels() func _adjust_ccs_text_scale(delta: float) -> void: CCSService.adjust_text_scale(delta) _refresh_text_scale_labels() func _refresh_text_scale_labels() -> void: if question_scale_value_label != null: var question_scale: float = AccountProfileService.preferences.get("questionTextScale", 1.0) question_scale_value_label.text = "%d%%" % round(question_scale * 100) if ccs_scale_value_label != null: ccs_scale_value_label.text = "%d%%" % round(CCSService.text_scale * 100) if core_block_size_value_label != null: core_block_size_value_label.text = str(BlockSessionService.core_block_size) func _on_theme_toggled(enabled: bool) -> void: UIThemeService.set_dark_mode(enabled) func _open_legal_document(kind: String) -> void: main.open_legal_document(kind) func _return_to_session() -> void: if return_screen.is_empty(): return main.show_screen(return_screen) func _on_theme_changed(is_dark: bool) -> void: theme_toggle.set_pressed_no_signal(is_dark) var panel := theme_toggle.get_parent().get_parent() as PanelContainer if panel != null: panel.add_theme_stylebox_override("panel", Brand.panel_box(is_dark)) for node in get_children(): if node is PanelContainer and node.has_meta("legal_panel"): node.add_theme_stylebox_override("panel", Brand.panel_box(is_dark))