Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b2d8b6f81 | ||
|
|
9402a5d6de | ||
|
|
bf56f4192f | ||
|
|
18802f4e8d |
@@ -60,6 +60,8 @@ var stem_plain_text := ""
|
|||||||
var explanation_plain_text := ""
|
var explanation_plain_text := ""
|
||||||
var highlight_drag_source := ""
|
var highlight_drag_source := ""
|
||||||
var timer_color_state := ""
|
var timer_color_state := ""
|
||||||
|
var last_timer_bar_value := -1.0
|
||||||
|
var last_timer_display_seconds := -1
|
||||||
var pending_correct_advance := false
|
var pending_correct_advance := false
|
||||||
var question_text_scale := 1.0
|
var question_text_scale := 1.0
|
||||||
var presented_choices: Array = []
|
var presented_choices: Array = []
|
||||||
@@ -1268,6 +1270,8 @@ func _load_question() -> void:
|
|||||||
flagged = false
|
flagged = false
|
||||||
selected_choice = ""
|
selected_choice = ""
|
||||||
current_answer = {}
|
current_answer = {}
|
||||||
|
last_timer_bar_value = -1.0
|
||||||
|
last_timer_display_seconds = -1
|
||||||
flag_button.button_pressed = false
|
flag_button.button_pressed = false
|
||||||
_refresh_toolbar_state()
|
_refresh_toolbar_state()
|
||||||
_clear_answer_review()
|
_clear_answer_review()
|
||||||
@@ -1452,15 +1456,12 @@ func _skip_current_question() -> void:
|
|||||||
return
|
return
|
||||||
if BlockSessionService.session_kind != "core":
|
if BlockSessionService.session_kind != "core":
|
||||||
if BlockSessionService.session_kind == "simulation":
|
if BlockSessionService.session_kind == "simulation":
|
||||||
|
selected_choice = ""
|
||||||
|
current_answer = {}
|
||||||
_save_simulation_current_response()
|
_save_simulation_current_response()
|
||||||
var target_index := _next_revisitable_index()
|
var target_index := _next_pending_simulation_index() if BlockSessionService.session_kind == "simulation" else _next_revisitable_index()
|
||||||
if target_index >= 0 and BlockSessionService.move_to(target_index):
|
if target_index >= 0 and BlockSessionService.move_to(target_index):
|
||||||
_load_question()
|
_load_question()
|
||||||
elif BlockSessionService.session_kind == "simulation" and not simulation_submit_pending:
|
|
||||||
simulation_submit_pending = true
|
|
||||||
next_button.disabled = true
|
|
||||||
next_button.text = "Submitting..."
|
|
||||||
ExamSimulationService.submit_block()
|
|
||||||
else:
|
else:
|
||||||
_finish()
|
_finish()
|
||||||
return
|
return
|
||||||
@@ -1492,6 +1493,18 @@ func _next_revisitable_index() -> int:
|
|||||||
return (BlockSessionService.current_index + 1) % question_count
|
return (BlockSessionService.current_index + 1) % question_count
|
||||||
|
|
||||||
|
|
||||||
|
func _next_pending_simulation_index() -> int:
|
||||||
|
var question_count := BlockSessionService.active_questions.size()
|
||||||
|
if question_count == 0:
|
||||||
|
return -1
|
||||||
|
for offset in range(1, question_count + 1):
|
||||||
|
var candidate := (BlockSessionService.current_index + offset) % question_count
|
||||||
|
var answer = BlockSessionService.get_answer_for_index(candidate)
|
||||||
|
if answer == null or str(answer.get("selected_choice_id", "")) == "":
|
||||||
|
return candidate
|
||||||
|
return -1
|
||||||
|
|
||||||
|
|
||||||
func _choice_gui_input(event: InputEvent, index: int) -> void:
|
func _choice_gui_input(event: InputEvent, index: int) -> void:
|
||||||
if pending_correct_advance or followup_required or _is_correct_answer_locked():
|
if pending_correct_advance or followup_required or _is_correct_answer_locked():
|
||||||
return
|
return
|
||||||
@@ -1539,12 +1552,15 @@ func _toggle_eliminate_selected() -> void:
|
|||||||
|
|
||||||
func _submit_or_next() -> void:
|
func _submit_or_next() -> void:
|
||||||
if BlockSessionService.session_kind == "simulation":
|
if BlockSessionService.session_kind == "simulation":
|
||||||
|
if selected_choice == "":
|
||||||
|
return
|
||||||
_save_simulation_current_response()
|
_save_simulation_current_response()
|
||||||
if BlockSessionService.current_index + 1 < BlockSessionService.active_questions.size():
|
var pending_index := _next_pending_simulation_index()
|
||||||
BlockSessionService.move_next()
|
if pending_index >= 0:
|
||||||
|
BlockSessionService.move_to(pending_index)
|
||||||
_load_question()
|
_load_question()
|
||||||
return
|
return
|
||||||
elif not simulation_submit_pending:
|
if not simulation_submit_pending:
|
||||||
simulation_submit_pending = true
|
simulation_submit_pending = true
|
||||||
next_button.disabled = true
|
next_button.disabled = true
|
||||||
next_button.text = "Submitting..."
|
next_button.text = "Submitting..."
|
||||||
@@ -1706,6 +1722,9 @@ func _on_random_question_failed(message: String) -> void:
|
|||||||
|
|
||||||
|
|
||||||
func _show_answer_review() -> void:
|
func _show_answer_review() -> void:
|
||||||
|
if BlockSessionService.session_kind == "simulation":
|
||||||
|
_clear_answer_review()
|
||||||
|
return
|
||||||
_show_explanation()
|
_show_explanation()
|
||||||
_show_references(BlockSessionService.get_current_question().get("references", []))
|
_show_references(BlockSessionService.get_current_question().get("references", []))
|
||||||
_ensure_answer_review_followups()
|
_ensure_answer_review_followups()
|
||||||
@@ -1844,6 +1863,9 @@ func _render_review_followups() -> void:
|
|||||||
if review_followups_content == null:
|
if review_followups_content == null:
|
||||||
return
|
return
|
||||||
_clear_container_children(review_followups_content)
|
_clear_container_children(review_followups_content)
|
||||||
|
if BlockSessionService.session_kind == "simulation":
|
||||||
|
review_followups_section.visible = false
|
||||||
|
return
|
||||||
if not BlockSessionService.show_review_followups:
|
if not BlockSessionService.show_review_followups:
|
||||||
review_followups_section.visible = false
|
review_followups_section.visible = false
|
||||||
return
|
return
|
||||||
@@ -2076,7 +2098,10 @@ func _update_timer_bar() -> void:
|
|||||||
return
|
return
|
||||||
var is_core_timer := BlockSessionService.session_kind == "core"
|
var is_core_timer := BlockSessionService.session_kind == "core"
|
||||||
var ratio := BlockSessionService.get_session_timer_ratio()
|
var ratio := BlockSessionService.get_session_timer_ratio()
|
||||||
timer_bar.value = ratio * 100.0
|
var next_value := ratio * 100.0
|
||||||
|
if last_timer_bar_value < 0.0 or absf(next_value - last_timer_bar_value) >= 0.1:
|
||||||
|
last_timer_bar_value = next_value
|
||||||
|
timer_bar.value = next_value
|
||||||
if is_core_timer and timer_label.visible:
|
if is_core_timer and timer_label.visible:
|
||||||
_set_timer_display(int(floor(BlockSessionService.get_core_question_total_elapsed_seconds())))
|
_set_timer_display(int(floor(BlockSessionService.get_core_question_total_elapsed_seconds())))
|
||||||
var color_state := "blue"
|
var color_state := "blue"
|
||||||
@@ -2096,6 +2121,9 @@ func _set_timer_display(total_seconds: int) -> void:
|
|||||||
if timer_label == null:
|
if timer_label == null:
|
||||||
return
|
return
|
||||||
var safe_seconds := maxi(0, total_seconds)
|
var safe_seconds := maxi(0, total_seconds)
|
||||||
|
if safe_seconds == last_timer_display_seconds:
|
||||||
|
return
|
||||||
|
last_timer_display_seconds = safe_seconds
|
||||||
timer_label.text = "%02d:%02d" % [int(floor(float(safe_seconds) / 60.0)), safe_seconds % 60]
|
timer_label.text = "%02d:%02d" % [int(floor(float(safe_seconds) / 60.0)), safe_seconds % 60]
|
||||||
|
|
||||||
|
|
||||||
@@ -3327,7 +3355,8 @@ func _refresh_submit_state() -> void:
|
|||||||
if next_button == null:
|
if next_button == null:
|
||||||
return
|
return
|
||||||
if BlockSessionService.session_kind == "simulation":
|
if BlockSessionService.session_kind == "simulation":
|
||||||
next_button.disabled = false
|
next_button.disabled = selected_choice == ""
|
||||||
|
next_button.text = "Submit Block" if selected_choice != "" and _next_pending_simulation_index() < 0 else "Next"
|
||||||
return
|
return
|
||||||
if pending_correct_advance or followup_required:
|
if pending_correct_advance or followup_required:
|
||||||
next_button.disabled = true
|
next_button.disabled = true
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ func _initialize() -> void:
|
|||||||
_test_feedback_context_excludes_study_content()
|
_test_feedback_context_excludes_study_content()
|
||||||
_test_feedback_form_validation()
|
_test_feedback_form_validation()
|
||||||
_test_revisitable_navigation_prefers_unanswered_questions()
|
_test_revisitable_navigation_prefers_unanswered_questions()
|
||||||
|
_test_timer_text_avoids_redundant_frame_updates()
|
||||||
_test_submit_styles_keep_stable_geometry()
|
_test_submit_styles_keep_stable_geometry()
|
||||||
_test_core_timer_counts_up_and_remains_visible()
|
_test_core_timer_counts_up_and_remains_visible()
|
||||||
await _test_player_runtime_construction()
|
await _test_player_runtime_construction()
|
||||||
@@ -341,12 +342,34 @@ func _test_revisitable_navigation_prefers_unanswered_questions() -> void:
|
|||||||
session.answers[1] = {"selected_choice_id": "B"}
|
session.answers[1] = {"selected_choice_id": "B"}
|
||||||
session.current_index = 2
|
session.current_index = 2
|
||||||
_assert(player._next_revisitable_index() == 0, "skip should wrap rather than reload the same unanswered question")
|
_assert(player._next_revisitable_index() == 0, "skip should wrap rather than reload the same unanswered question")
|
||||||
|
session.answers = [{"selected_choice_id": "A"}, null, {"selected_choice_id": "C"}]
|
||||||
|
session.current_index = 2
|
||||||
|
_assert(player._next_pending_simulation_index() == 1, "simulation should cycle an earlier skipped question back")
|
||||||
|
session.answers[1] = {"selected_choice_id": "B"}
|
||||||
|
_assert(player._next_pending_simulation_index() == -1, "simulation should submit only after every question has an answer")
|
||||||
|
session.answers[2] = {"selected_choice_id": ""}
|
||||||
|
session.current_index = 2
|
||||||
|
_assert(player._next_pending_simulation_index() == 2, "the only skipped simulation question should remain pending")
|
||||||
player.free()
|
player.free()
|
||||||
session.active_questions = prior_questions
|
session.active_questions = prior_questions
|
||||||
session.answers = prior_answers
|
session.answers = prior_answers
|
||||||
session.current_index = prior_index
|
session.current_index = prior_index
|
||||||
|
|
||||||
|
|
||||||
|
func _test_timer_text_avoids_redundant_frame_updates() -> void:
|
||||||
|
var player = load("res://scripts/ui/question_player.gd").new()
|
||||||
|
player.timer_label = Label.new()
|
||||||
|
player._set_timer_display(65)
|
||||||
|
_assert(player.timer_label.text == "01:05", "timer should render a changed second")
|
||||||
|
player.timer_label.text = "unchanged"
|
||||||
|
player._set_timer_display(65)
|
||||||
|
_assert(player.timer_label.text == "unchanged", "timer should not rewrite its label every frame")
|
||||||
|
player._set_timer_display(66)
|
||||||
|
_assert(player.timer_label.text == "01:06", "timer should render the next changed second")
|
||||||
|
player.timer_label.free()
|
||||||
|
player.free()
|
||||||
|
|
||||||
|
|
||||||
func _test_player_runtime_construction() -> void:
|
func _test_player_runtime_construction() -> void:
|
||||||
var host := Control.new()
|
var host := Control.new()
|
||||||
root.add_child(host)
|
root.add_child(host)
|
||||||
|
|||||||
Reference in New Issue
Block a user