extends VBoxContainer const Style := preload("res://scripts/ui/ui_style.gd") const Brand := preload("res://scripts/ui/medmind_style.gd") const ICON_ACTIVITY := preload("res://assets/icons/lucide/activity.svg") const ICON_HEART := preload("res://assets/icons/lucide/heart-pulse.svg") const ICON_LUNGS := preload("res://assets/icons/lucide/lungs.svg") const ICON_SEARCH := preload("res://assets/icons/lucide/search.svg") const ICON_STETHOSCOPE := preload("res://assets/icons/lucide/stethoscope.svg") const ICON_THERMOMETER := preload("res://assets/icons/lucide/thermometer.svg") const RESPONSIVE_BREAKPOINT := 1050.0 var main: Control var header_panel: PanelContainer var header_layout: BoxContainer var header_icon_panel: PanelContainer var heading: Label var toolbar: HFlowContainer var workspace: BoxContainer var chart_panel: PanelContainer var clerk_panel: PanelContainer var summary: RichTextLabel var history: RichTextLabel var appearance: RichTextLabel var chart: RichTextLabel var vitals_flow: HFlowContainer var orders: VBoxContainer var search: LineEdit var search_button: Button var search_results_panel: PanelContainer var search_results: VBoxContainer var actions: VBoxContainer var locations: HFlowContainer var time_row: GridContainer var status: Label var final_orders_notice: Label var clock: Label var timer: Timer var end_button: Button var report_dialog: Control var report_button: Button var app_feedback_button: Button var leave_button: Button var leaving := false var theme_service var theme_button: Button var section_labels: Array[Label] = [] var toolbar_buttons: Array[Button] = [] func _ready() -> void: theme_service = get_node("/root/UIThemeService") set_anchors_preset(Control.PRESET_FULL_RECT) add_theme_constant_override("separation", 10) _build_header() _build_workspace() _build_runtime_services() resized.connect(_update_responsive_layout) if not theme_service.theme_changed.is_connected(_on_theme_changed): theme_service.theme_changed.connect(_on_theme_changed) _apply_theme() call_deferred("_update_responsive_layout") func _build_header() -> void: header_panel = PanelContainer.new() add_child(header_panel) var margin := MarginContainer.new() margin.add_theme_constant_override("margin_left", 12) margin.add_theme_constant_override("margin_right", 12) margin.add_theme_constant_override("margin_top", 8) margin.add_theme_constant_override("margin_bottom", 8) header_panel.add_child(margin) header_layout = BoxContainer.new() header_layout.vertical = false header_layout.add_theme_constant_override("separation", 12) margin.add_child(header_layout) var title_row := HBoxContainer.new() title_row.size_flags_horizontal = Control.SIZE_EXPAND_FILL title_row.add_theme_constant_override("separation", 10) header_layout.add_child(title_row) header_icon_panel = PanelContainer.new() header_icon_panel.custom_minimum_size = Vector2(38, 38) title_row.add_child(header_icon_panel) var header_icon := TextureRect.new() header_icon.texture = ICON_STETHOSCOPE header_icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE header_icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED header_icon.custom_minimum_size = Vector2(22, 22) header_icon.modulate = Brand.SKY_BLUE header_icon_panel.add_child(header_icon) heading = Label.new() heading.text = "CCS Case" heading.size_flags_horizontal = Control.SIZE_EXPAND_FILL _set_label(heading, 18, Brand.text_color(true)) title_row.add_child(heading) toolbar = HFlowContainer.new() toolbar.size_flags_horizontal = Control.SIZE_SHRINK_END toolbar.custom_minimum_size.x = 440 toolbar.add_theme_constant_override("h_separation", 8) toolbar.add_theme_constant_override("v_separation", 8) header_layout.add_child(toolbar) var smaller := _toolbar_button("A-", "Decrease case text size") smaller.pressed.connect(_zoom.bind(-CCSService.TEXT_SCALE_STEP)) var larger := _toolbar_button("A+", "Increase case text size") larger.pressed.connect(_zoom.bind(CCSService.TEXT_SCALE_STEP)) var clear_highlights := _toolbar_button("Clear", "Clear case highlights") clear_highlights.pressed.connect(_clear_highlights) report_button = _toolbar_button("Report", "Report this CCS case") report_button.pressed.connect(_report) app_feedback_button = _toolbar_button("Feedback", "Send general feedback or report a bug") app_feedback_button.pressed.connect(func(): main.open_feedback_dialog()) theme_button = _toolbar_button("", "Switch color theme") theme_button.custom_minimum_size = Vector2(44, 38) UISoundService.set_button_sound(theme_button, "toggle") theme_button.pressed.connect(theme_service.toggle_theme) func _toolbar_button(text_value: String, tooltip: String) -> Button: var button := Button.new() button.text = text_value button.tooltip_text = tooltip button.custom_minimum_size = Vector2(54, 38) button.add_theme_font_override("font", Brand.font) button.add_theme_font_size_override("font_size", 14) toolbar.add_child(button) toolbar_buttons.append(button) return button func _build_workspace() -> void: var page_scroll := ScrollContainer.new() page_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL page_scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED add_child(page_scroll) workspace = BoxContainer.new() workspace.size_flags_horizontal = Control.SIZE_EXPAND_FILL workspace.size_flags_vertical = Control.SIZE_EXPAND_FILL workspace.add_theme_constant_override("separation", 10) page_scroll.add_child(workspace) _build_chart_panel() _build_order_clerk() func _build_chart_panel() -> void: chart_panel = PanelContainer.new() chart_panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL chart_panel.size_flags_vertical = Control.SIZE_EXPAND_FILL workspace.add_child(chart_panel) var margin := MarginContainer.new() margin.add_theme_constant_override("margin_left", 18) margin.add_theme_constant_override("margin_right", 18) margin.add_theme_constant_override("margin_top", 16) margin.add_theme_constant_override("margin_bottom", 18) chart_panel.add_child(margin) var content := VBoxContainer.new() content.add_theme_constant_override("separation", 14) margin.add_child(content) summary = _clinical_text("summary", 17) content.add_child(summary) vitals_flow = HFlowContainer.new() vitals_flow.add_theme_constant_override("h_separation", 10) vitals_flow.add_theme_constant_override("v_separation", 8) content.add_child(vitals_flow) content.add_child(HSeparator.new()) _add_chart_section(content, "History", "history") history = _clinical_text("history", 16) content.add_child(history) _add_chart_section(content, "Appearance", "appearance") appearance = _clinical_text("appearance", 16) content.add_child(appearance) _add_chart_section(content, "Chart", "chart") chart = _clinical_text("chart", 16) content.add_child(chart) func _add_chart_section(parent: VBoxContainer, text_value: String, region: String) -> void: var label := Label.new() label.name = "%sHeading" % region.capitalize() label.text = text_value _set_label(label, 15, Brand.PURPLE) parent.add_child(label) section_labels.append(label) func _clinical_text(region: String, font_size: int) -> RichTextLabel: var label := RichTextLabel.new() label.bbcode_enabled = true label.selection_enabled = true label.fit_content = true label.scroll_active = false label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART label.add_theme_font_override("normal_font", Brand.font) label.add_theme_font_override("bold_font", Brand.font) label.add_theme_font_size_override("normal_font_size", font_size) label.add_theme_font_size_override("bold_font_size", font_size) label.set_meta("ccs_base_font_size", font_size) label.set_meta("ccs_region", region) label.gui_input.connect(_text_input.bind(label)) return label func _build_order_clerk() -> void: clerk_panel = PanelContainer.new() clerk_panel.custom_minimum_size = Vector2(370, 0) clerk_panel.size_flags_vertical = Control.SIZE_EXPAND_FILL workspace.add_child(clerk_panel) var margin := MarginContainer.new() margin.add_theme_constant_override("margin_left", 14) margin.add_theme_constant_override("margin_right", 14) margin.add_theme_constant_override("margin_top", 12) margin.add_theme_constant_override("margin_bottom", 14) clerk_panel.add_child(margin) var clerk := VBoxContainer.new() clerk.add_theme_constant_override("separation", 8) margin.add_child(clerk) var order_title := Label.new() order_title.text = "Order Clerk" _set_label(order_title, 18, Brand.PURPLE) clerk.add_child(order_title) section_labels.append(order_title) var search_help := Label.new() search_help.text = "Search tests, treatments, and procedures" _set_label(search_help, 12, Brand.muted_color(true)) search_help.set_meta("ccs_muted_label", true) clerk.add_child(search_help) var search_row := HBoxContainer.new() search_row.add_theme_constant_override("separation", 6) clerk.add_child(search_row) search = LineEdit.new() search.placeholder_text = "Search..." search.size_flags_horizontal = Control.SIZE_EXPAND_FILL search.text_submitted.connect(func(_value: String): _submit_search()) search_row.add_child(search) search_button = Button.new() search_button.icon = ICON_SEARCH search_button.tooltip_text = "Search order catalog" search_button.custom_minimum_size = Vector2(42, 42) search_button.pressed.connect(_submit_search) search_row.add_child(search_button) search_results_panel = PanelContainer.new() search_results_panel.visible = false clerk.add_child(search_results_panel) var results_scroll := ScrollContainer.new() results_scroll.custom_minimum_size.y = 150 search_results_panel.add_child(results_scroll) search_results = VBoxContainer.new() search_results.size_flags_horizontal = Control.SIZE_EXPAND_FILL search_results.add_theme_constant_override("separation", 5) results_scroll.add_child(search_results) var active_title := _clerk_heading("Active orders") clerk.add_child(active_title) orders = VBoxContainer.new() orders.add_theme_constant_override("separation", 5) clerk.add_child(orders) var exam_title := _clerk_heading("Physical examination") clerk.add_child(exam_title) actions = VBoxContainer.new() actions.add_theme_constant_override("separation", 5) clerk.add_child(actions) locations = HFlowContainer.new() locations.add_theme_constant_override("h_separation", 5) locations.add_theme_constant_override("v_separation", 5) clerk.add_child(locations) time_row = GridContainer.new() time_row.columns = 4 time_row.add_theme_constant_override("h_separation", 5) time_row.add_theme_constant_override("v_separation", 5) clerk.add_child(time_row) for minutes in [1, 10, 60, 1440]: var button := Button.new() button.text = "+%s" % ("1 day" if minutes == 1440 else "%d min" % minutes) button.size_flags_horizontal = Control.SIZE_EXPAND_FILL button.pressed.connect(CCSService.send_command.bind("advance", "", minutes)) time_row.add_child(button) var reassess := Button.new() reassess.text = "Interval history" reassess.pressed.connect(CCSService.send_command.bind("interval_history", "", 0)) clerk.add_child(reassess) end_button = Button.new() end_button.text = "End case" end_button.pressed.connect(_end_case) clerk.add_child(end_button) leave_button = Button.new() leave_button.text = "Leave case" leave_button.pressed.connect(_leave) clerk.add_child(leave_button) final_orders_notice = Label.new() final_orders_notice.visible = false final_orders_notice.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART clerk.add_child(final_orders_notice) clock = Label.new() clock.visible = false clerk.add_child(clock) status = Label.new() status.visible = false status.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART clerk.add_child(status) func _clerk_heading(text_value: String) -> Label: var label := Label.new() label.text = text_value _set_label(label, 13, Brand.PURPLE) section_labels.append(label) return label func _build_runtime_services() -> void: timer = Timer.new() timer.wait_time = 1 timer.timeout.connect(_tick) add_child(timer) CCSService.attempt_changed.connect(_render) CCSService.attempt_abandoned.connect(_on_abandoned) CCSService.orders_changed.connect(_render_search) CCSService.request_failed.connect(_request_failed) CCSService.report_submitted.connect(_report_submitted) report_dialog = preload("res://scripts/ui/ccs_report_dialog.gd").new() add_child(report_dialog) func on_show(payload: Variant = null) -> void: _set_status("") leaving = false leave_button.disabled = false if typeof(payload) == TYPE_STRING and not str(payload).is_empty(): CCSService.load_attempt(str(payload)) _render() timer.start() func on_hide() -> void: timer.stop() func _render() -> void: var attempt: Dictionary = CCSService.attempt if attempt.is_empty(): return if str(attempt.get("state", "")) == "completed": main.show_screen("ccs_review", attempt) return heading.text = "CCS Case | %s" % _humanize_code(str(attempt.get("location", ""))) _render_study_text() _render_vitals() _render_orders() _render_commands() var final_orders := str(attempt.get("state", "")) == "final_orders" end_button.text = "Submit final orders" if final_orders else "End case" time_row.visible = not final_orders final_orders_notice.visible = final_orders final_orders_notice.text = "Finalize or cancel patient-care orders, then submit." _tick() _apply_text_scale() func _render_vitals() -> void: _clear(vitals_flow) var vitals: Dictionary = CCSService.attempt.get("vitals", {}) for key in _ordered_vital_keys(vitals): var chip := PanelContainer.new() chip.add_theme_stylebox_override("panel", _vital_chip_box()) vitals_flow.add_child(chip) var row := HBoxContainer.new() row.add_theme_constant_override("separation", 7) chip.add_child(row) var icon := TextureRect.new() icon.texture = _vital_icon(key) icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED icon.custom_minimum_size = Vector2(18, 18) icon.modulate = _vital_color(key) row.add_child(icon) var label := Label.new() label.text = "%s: %s" % [_vital_label(key), str(vitals[key])] _set_label(label, 13, Brand.text_color(_is_dark())) row.add_child(label) func _ordered_vital_keys(vitals: Dictionary) -> Array[String]: var ordered: Array[String] = [] for key in ["temperature", "bloodPressure", "pulse", "respirations", "oxygenSaturation"]: if vitals.has(key): ordered.append(key) var remaining: Array[String] = [] for raw_key in vitals.keys(): var key := str(raw_key) if not key in ordered: remaining.append(key) remaining.sort() ordered.append_array(remaining) return ordered func _vital_label(key: String) -> String: match key: "bloodPressure": return "Blood Pressure" "oxygenSaturation": return "Oxygen Saturation" _: return _humanize_code(key) func _vital_icon(key: String) -> Texture2D: match key: "temperature": return ICON_THERMOMETER "bloodPressure", "pulse": return ICON_HEART "respirations": return ICON_LUNGS _: return ICON_ACTIVITY func _vital_color(key: String) -> Color: match key: "temperature", "oxygenSaturation": return Brand.SKY_BLUE "bloodPressure", "pulse": return Color("fb7185") _: return Brand.PURPLE func _render_orders() -> void: _clear(orders) var active_codes: Array = CCSService.attempt.get("activeOrderCodes", []) if active_codes.is_empty(): var empty := Label.new() empty.text = "No active orders" _set_label(empty, 12, Brand.muted_color(_is_dark())) orders.add_child(empty) return for raw_code in active_codes: var code := str(raw_code) var row := HBoxContainer.new() row.add_theme_constant_override("separation", 6) orders.add_child(row) var label := Label.new() label.text = _humanize_code(code) label.size_flags_horizontal = Control.SIZE_EXPAND_FILL label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART _set_label(label, 13, Brand.text_color(_is_dark())) row.add_child(label) var cancel := Button.new() cancel.text = "Cancel" cancel.pressed.connect(CCSService.send_command.bind("cancel_order", code, 0)) _style_secondary_button(cancel) row.add_child(cancel) func _render_commands() -> void: _clear(actions) for exam in CCSService.attempt.get("availableExams", []): if typeof(exam) != TYPE_DICTIONARY: continue var button := Button.new() button.text = str(exam.get("name", _humanize_code(str(exam.get("code", "Exam"))))) button.pressed.connect(CCSService.send_command.bind("exam", str(exam.get("code", "")), 0)) _style_secondary_button(button) actions.add_child(button) _clear(locations) for raw_location in CCSService.attempt.get("allowedLocations", []): var location := str(raw_location) var button := Button.new() button.text = "Move to %s" % _humanize_code(location) button.custom_minimum_size.x = 150 button.size_flags_horizontal = Control.SIZE_EXPAND_FILL button.pressed.connect(CCSService.send_command.bind("location", location, 0)) _style_secondary_button(button) locations.add_child(button) _style_command_buttons() func _submit_search() -> void: if search.text.strip_edges().length() < 2: _set_status("Enter at least two characters to search.") return _set_status("") CCSService.search_orders(search.text) func _render_search() -> void: _clear(search_results) search_results_panel.visible = not CCSService.orders.is_empty() for order in CCSService.orders: if typeof(order) != TYPE_DICTIONARY: continue var button := Button.new() button.text = "%s (%s)" % [order.get("canonicalName", ""), _humanize_code(str(order.get("category", "")))] button.alignment = HORIZONTAL_ALIGNMENT_LEFT button.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART button.pressed.connect(_place_order.bind(str(order.get("code", "")))) _style_secondary_button(button) search_results.add_child(button) func _place_order(code: String) -> void: CCSService.send_command("order", code) search.text = "" CCSService.orders = [] _render_search() func _tick() -> void: var deadline := CCSService.deadline_unix() var final_orders := str(CCSService.attempt.get("state", "")) == "final_orders" clock.visible = final_orders and deadline > 0 if deadline <= 0: clock.text = "" return var remaining := maxi(0, int(ceil(deadline - Time.get_unix_time_from_system()))) if final_orders: clock.text = "Final orders: %02d:%02d remaining" % [floori(float(remaining) / 60.0), remaining % 60] if remaining == 0 and not CCSService.is_loading: CCSService.load_attempt(str(CCSService.attempt.get("id", ""))) func leave_case() -> void: _leave() func _leave() -> void: if leaving or CCSService.attempt.is_empty(): return leaving = true leave_button.disabled = true _set_status("Leaving case...", false) CCSService.abandon() func _on_abandoned() -> void: if not leaving: return leaving = false main.show_screen("selection", "step3") func _request_failed(message: String) -> void: if not visible: return _set_status(message) if leaving: leaving = false leave_button.disabled = false func _end_case() -> void: CCSService.send_command("exit_case" if str(CCSService.attempt.get("state", "")) == "final_orders" else "end_case") func _clear(parent: Node) -> void: for child in parent.get_children(): parent.remove_child(child) child.queue_free() func _chart_text(entries: Array) -> String: var lines: Array[String] = [] for entry in entries: if typeof(entry) != TYPE_DICTIONARY: continue var category := str(entry.get("category", "note")) var category_label := "In Progress" if category == "progress" else _humanize_code(category) lines.append("T+%d (%s): %s" % [entry.get("simulatedMinute", 0), category_label, entry.get("text", "")]) return "\n\n".join(lines) func _summary_plain() -> String: var attempt := CCSService.attempt return "%s\n%s" % [attempt.get("patientSummary", ""), attempt.get("reasonForEncounter", "")] func _render_study_text() -> void: var attempt_id := str(CCSService.attempt.get("id", "")) summary.text = CCSService.highlighted_text(attempt_id, "summary", _summary_plain()) history.text = CCSService.highlighted_text(attempt_id, "history", str(CCSService.attempt.get("history", ""))) appearance.text = CCSService.highlighted_text(attempt_id, "appearance", str(CCSService.attempt.get("appearance", ""))) chart.text = CCSService.highlighted_text(attempt_id, "chart", _chart_text(CCSService.attempt.get("chart", []))) func _text_input(event: InputEvent, label: RichTextLabel) -> void: var mouse := event as InputEventMouseButton if mouse != null and mouse.button_index == MOUSE_BUTTON_LEFT and not mouse.pressed: call_deferred("_commit_selection", label) func _commit_selection(label: RichTextLabel) -> void: var selected := label.get_selected_text().strip_edges() if selected.is_empty(): return var start := label.get_selection_from() var end := label.get_selection_to() CCSService.toggle_highlight( str(CCSService.attempt.get("id", "")), str(label.get_meta("ccs_region", "")), mini(start, end), maxi(start, end) ) label.deselect() _render_study_text() func _zoom(delta: float) -> void: CCSService.adjust_text_scale(delta) _apply_text_scale() func _apply_text_scale() -> void: for label in [summary, history, appearance, chart]: var base_size := int(label.get_meta("ccs_base_font_size", 16)) var font_size := int(round(float(base_size) * CCSService.text_scale)) label.add_theme_font_size_override("normal_font_size", font_size) label.add_theme_font_size_override("bold_font_size", font_size) func _clear_highlights() -> void: CCSService.clear_highlights(str(CCSService.attempt.get("id", ""))) _render_study_text() func _report() -> void: report_dialog.show_for(str(CCSService.attempt.get("id", ""))) func _report_submitted(_id: String) -> void: report_button.tooltip_text = "Report submitted; report another issue" _style_outline_button(report_button, Brand.SUCCESS) func _set_status(message: String, is_error: bool = true) -> void: if status == null: return status.text = message status.visible = not message.is_empty() status.add_theme_color_override("font_color", Color("fb7185") if is_error else Brand.SKY_BLUE) func _humanize_code(value: String) -> String: return value.replace("_", " ").replace("-", " ").capitalize() func _update_responsive_layout() -> void: if workspace == null or header_layout == null: return var narrow := size.x > 0 and size.x < RESPONSIVE_BREAKPOINT workspace.vertical = narrow header_layout.vertical = narrow toolbar.custom_minimum_size.x = 0 if narrow else 440 toolbar.size_flags_horizontal = Control.SIZE_EXPAND_FILL if narrow else Control.SIZE_SHRINK_END chart_panel.custom_minimum_size.x = 0 if narrow else 600 clerk_panel.custom_minimum_size.x = 0 if narrow else 370 clerk_panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL if narrow else Control.SIZE_SHRINK_END queue_sort() func _on_theme_changed(_is_dark_mode: bool) -> void: call_deferred("_finish_theme_change") func _finish_theme_change() -> void: _apply_theme() if visible and not CCSService.attempt.is_empty(): _render() func _apply_theme() -> void: var dark := _is_dark() if header_panel == null: return header_panel.add_theme_stylebox_override("panel", Brand.content_card_box(dark, false)) chart_panel.add_theme_stylebox_override("panel", Brand.content_card_box(dark, false)) clerk_panel.add_theme_stylebox_override("panel", Brand.content_card_box(dark, false)) header_icon_panel.add_theme_stylebox_override("panel", Brand.icon_circle_box(dark, Brand.PURPLE, 6, true)) heading.add_theme_color_override("font_color", Brand.text_color(dark)) for label in section_labels: label.add_theme_color_override("font_color", Brand.PURPLE if dark else Brand.ROYAL_BLUE) for node in find_children("*", "Label", true, false): var label := node as Label if label.has_meta("ccs_muted_label"): label.add_theme_color_override("font_color", Brand.muted_color(dark)) for label in [summary, history, appearance, chart]: label.add_theme_color_override("default_color", Brand.text_color(dark)) search.add_theme_font_override("font", Brand.font) search.add_theme_color_override("font_color", Brand.text_color(dark)) search.add_theme_color_override("font_placeholder_color", Brand.muted_color(dark)) search.add_theme_stylebox_override("normal", _compact_field_box(dark)) search.add_theme_stylebox_override("focus", _compact_field_box(dark, true)) search_results_panel.add_theme_stylebox_override("panel", Brand.content_card_box(dark, false)) for button in toolbar_buttons: _style_secondary_button(button) _style_icon_button(search_button) _style_outline_button(end_button, Brand.AMBER) _style_outline_button(leave_button, Color("fb7185")) for button in time_row.get_children(): if button is Button: _style_secondary_button(button) Brand.set_theme_toggle_icon(theme_button, dark) theme_button.tooltip_text = "Switch to light mode" if dark else "Switch to dark mode" final_orders_notice.add_theme_color_override("font_color", Brand.AMBER) clock.add_theme_color_override("font_color", Brand.AMBER) _render_vitals() func _style_command_buttons() -> void: for container in [actions, locations]: for child in container.get_children(): if child is Button: _style_secondary_button(child) var reassess := time_row.get_parent().get_child(time_row.get_index() + 1) as Button if reassess != null: _style_secondary_button(reassess) func _style_secondary_button(button: Button) -> void: var dark := _is_dark() button.add_theme_font_override("font", Brand.font) button.add_theme_font_size_override("font_size", 13) button.add_theme_color_override("font_color", Brand.text_color(dark)) button.add_theme_color_override("font_hover_color", Brand.text_color(dark)) button.add_theme_stylebox_override("normal", _compact_button_box(dark)) button.add_theme_stylebox_override("hover", _compact_button_box(dark, true)) button.add_theme_stylebox_override("pressed", _compact_button_box(dark, true)) func _style_icon_button(button: Button) -> void: _style_secondary_button(button) button.add_theme_constant_override("icon_max_width", 20) var color := Brand.SKY_BLUE if _is_dark() else Brand.ROYAL_BLUE for color_name in ["icon_normal_color", "icon_hover_color", "icon_pressed_color", "icon_focus_color"]: button.add_theme_color_override(color_name, color) func _style_outline_button(button: Button, accent: Color) -> void: var dark := _is_dark() var normal := _compact_button_box(dark) normal.border_color = accent var hover := normal.duplicate() as StyleBoxFlat hover.bg_color = Color(accent, 0.1) button.add_theme_font_override("font", Brand.font) button.add_theme_font_size_override("font_size", 13) button.add_theme_color_override("font_color", accent) button.add_theme_color_override("font_hover_color", accent) button.add_theme_stylebox_override("normal", normal) button.add_theme_stylebox_override("hover", hover) button.add_theme_stylebox_override("pressed", hover) func _compact_field_box(dark: bool, focused: bool = false) -> StyleBoxFlat: var box := StyleBoxFlat.new() box.bg_color = Color("0d172a") if dark else Brand.LIGHT_SURFACE box.border_color = Brand.ROYAL_BLUE if focused else (Color("33415d") if dark else Brand.LIGHT_BORDER) box.set_border_width_all(1) box.set_corner_radius_all(6) box.content_margin_left = 12 box.content_margin_right = 12 box.content_margin_top = 8 box.content_margin_bottom = 8 return box func _compact_button_box(dark: bool, hover: bool = false) -> StyleBoxFlat: var box := _compact_field_box(dark, hover) box.bg_color = Color("111b30") if dark else Brand.LIGHT_SURFACE return box func _vital_chip_box() -> StyleBoxFlat: var dark := _is_dark() var box := StyleBoxFlat.new() box.bg_color = Color("111b30") if dark else Color("f8faff") box.border_color = Color("33415d") if dark else Brand.LIGHT_BORDER box.set_border_width_all(1) box.set_corner_radius_all(6) box.content_margin_left = 10 box.content_margin_right = 10 box.content_margin_top = 8 box.content_margin_bottom = 8 return box func _set_label(label: Label, font_size: int, color: Color) -> void: label.add_theme_font_override("font", Brand.font) label.add_theme_font_size_override("font_size", font_size) label.add_theme_color_override("font_color", color) func _is_dark() -> bool: return theme_service != null and theme_service.is_dark_mode()