Files
step3/scripts/ui/simulation_break.gd
hzandClaude Sonnet 5 bc9f68aaf2 Restyle the simulation break and review screens to match the results screen
Both were still on the legacy flat-panel Style system, which made
"Review Questions" on the redesigned results screen drop into a
visibly older-looking screen. Rebuilt both with the same Brand card/
badge/accent-color language: a hero card for the break screen, and
color-coded (green/red-tinted, bordered) choice cards, accent-colored
filter chips, and icon headings for the review screen. Pure re-skin -
same functions, same data flow, only what they render into changed.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-01 04:21:02 -05:00

195 lines
7.2 KiB
GDScript

extends VBoxContainer
const Brand := preload("res://scripts/ui/medmind_style.gd")
var main: Control
var card: PanelContainer
var eyebrow: Label
var title: Label
var detail: Label
var start_button: Button
var abandon_button: Button
var error_label: Label
var note: Label
var abandon_armed := false
func _ready() -> void:
set_anchors_preset(Control.PRESET_FULL_RECT)
add_theme_constant_override("separation", 16)
if not ExamSimulationService.state_changed.is_connected(_on_state_changed):
ExamSimulationService.state_changed.connect(_on_state_changed)
if not ExamSimulationService.request_failed.is_connected(_on_failed):
ExamSimulationService.request_failed.connect(_on_failed)
card = PanelContainer.new()
add_child(card)
var card_margin := MarginContainer.new()
card_margin.add_theme_constant_override("margin_left", 24)
card_margin.add_theme_constant_override("margin_right", 24)
card_margin.add_theme_constant_override("margin_top", 22)
card_margin.add_theme_constant_override("margin_bottom", 22)
card.add_child(card_margin)
var card_content := VBoxContainer.new()
card_content.add_theme_constant_override("separation", 10)
card_margin.add_child(card_content)
eyebrow = Label.new()
eyebrow.text = "STEP 3 DAY 1 SIMULATION"
eyebrow.add_theme_font_override("font", Brand.font)
eyebrow.add_theme_font_size_override("font_size", 13)
card_content.add_child(eyebrow)
title = Label.new()
title.add_theme_font_override("font", Brand.font)
title.add_theme_font_size_override("font_size", 26)
card_content.add_child(title)
detail = Label.new()
detail.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
detail.add_theme_font_override("font", Brand.font)
detail.add_theme_font_size_override("font_size", 15)
card_content.add_child(detail)
var actions := HBoxContainer.new()
actions.add_theme_constant_override("separation", 12)
card_content.add_child(actions)
start_button = Button.new()
start_button.custom_minimum_size = Vector2(220, 52)
start_button.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
_style_gradient_button(start_button)
start_button.pressed.connect(_start_block)
actions.add_child(start_button)
abandon_button = Button.new()
abandon_button.text = "Abandon simulation"
abandon_button.custom_minimum_size = Vector2(200, 52)
abandon_button.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
abandon_button.pressed.connect(_abandon)
actions.add_child(abandon_button)
error_label = Label.new()
error_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
error_label.add_theme_font_override("font", Brand.font)
error_label.add_theme_font_size_override("font_size", 13)
error_label.add_theme_color_override("font_color", Brand.MAGENTA)
add_child(error_label)
note = Label.new()
note.text = "Each block runs for 30 minutes. You may review and change answers within the active block, but closed blocks cannot be reopened. Leaving during a block does not stop its server timer."
note.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
note.add_theme_font_override("font", Brand.font)
note.add_theme_font_size_override("font_size", 12)
add_child(note)
var spacer := Control.new()
spacer.size_flags_vertical = Control.SIZE_EXPAND_FILL
add_child(spacer)
UIThemeService.theme_changed.connect(func(_dark: bool): refresh_theme_controls())
refresh_theme_controls()
func refresh_theme_controls() -> void:
var dark := UIThemeService.is_dark_mode()
card.add_theme_stylebox_override("panel", Brand.content_card_box(dark, true, Brand.ROYAL_BLUE))
eyebrow.add_theme_color_override("font_color", Brand.SKY_BLUE)
title.add_theme_color_override("font_color", Brand.text_color(dark))
detail.add_theme_color_override("font_color", Brand.muted_color(dark))
note.add_theme_color_override("font_color", Brand.muted_color(dark))
_style_outline_button(abandon_button, Brand.MAGENTA, dark)
func on_show(_payload: Variant = null) -> void:
abandon_armed = false
_refresh()
func _refresh() -> void:
var block := int(ExamSimulationService.simulation.get("currentBlock", -1))
var break_seconds := int(ExamSimulationService.simulation.get("breakSecondsRemaining", 3300))
if block < 0:
title.text = "Foundations of Independent Practice"
detail.text = "232 questions across 12 timed blocks. Results and explanations remain hidden until the simulation is complete. Estimated break pool: %d minutes." % int(break_seconds / 60.0)
else:
title.text = "Block %d complete" % (block + 1)
detail.text = "%d of 12 blocks complete. Your progress is saved. Break pool: approximately %d minutes." % [block + 1, int(break_seconds / 60.0)]
start_button.text = "Start Block %d" % (block + 2)
start_button.disabled = ExamSimulationService.is_loading
abandon_button.text = "Confirm abandon" if abandon_armed else "Abandon simulation"
abandon_button.disabled = ExamSimulationService.is_loading
error_label.text = ""
func _start_block() -> void:
start_button.disabled = true
start_button.text = "Preparing..."
ExamSimulationService.start_next_block()
func _abandon() -> void:
if not abandon_armed:
abandon_armed = true
abandon_button.text = "Confirm abandon"
error_label.text = "This permanently closes the current simulation. Select Confirm abandon to continue."
return
abandon_button.disabled = true
ExamSimulationService.abandon()
func _on_state_changed() -> void:
if not visible:
return
if ExamSimulationService.simulation.is_empty():
main.show_screen("selection", "step3")
return
if str(ExamSimulationService.simulation.get("state", "")) != "block_active":
_refresh()
return
BlockSessionService.start_simulation_block(
ExamSimulationService.get_current_questions(),
int(ExamSimulationService.simulation.get("currentBlock", 0)),
int(ExamSimulationService.simulation.get("totalBlocks", 12)),
ExamSimulationService.get_block_deadline_unix())
BlockSessionService.restore_simulation_responses(ExamSimulationService.simulation.get("responses", []))
main.show_screen("player")
func _on_failed(message: String) -> void:
if not visible:
return
error_label.text = message
start_button.disabled = false
_refresh()
func _style_gradient_button(button: Button) -> void:
Brand.apply_font(button)
button.add_theme_font_size_override("font_size", 15)
button.add_theme_stylebox_override("normal", Brand.gradient_button_box())
button.add_theme_stylebox_override("hover", Brand.gradient_button_box())
button.add_theme_stylebox_override("pressed", Brand.gradient_button_box())
button.add_theme_color_override("font_color", Color.WHITE)
button.add_theme_color_override("font_hover_color", Color.WHITE)
func _style_outline_button(button: Button, accent: Color, dark: bool) -> void:
Brand.apply_font(button)
button.add_theme_font_size_override("font_size", 14)
var normal := StyleBoxFlat.new()
normal.bg_color = Color(0, 0, 0, 0)
normal.border_color = accent
normal.set_border_width_all(1)
normal.set_corner_radius_all(10)
var hover := StyleBoxFlat.new()
hover.bg_color = Color(accent, 0.12)
hover.border_color = accent
hover.set_border_width_all(1)
hover.set_corner_radius_all(10)
button.add_theme_stylebox_override("normal", normal)
button.add_theme_stylebox_override("hover", hover)
button.add_theme_stylebox_override("pressed", hover)
button.add_theme_color_override("font_color", accent if dark else accent.darkened(0.08))
button.add_theme_color_override("font_hover_color", accent)