Add question image media support

This commit is contained in:
hz
2026-07-11 03:12:46 -05:00
parent 5ed96ae3b8
commit 9c8775d740
5 changed files with 215 additions and 3 deletions
+115 -2
View File
@@ -39,11 +39,13 @@ var flag_button: Button
var report_button: Button
var nav_palette: HBoxContainer
var question_content: VBoxContainer
var media_container: VBoxContainer
var timer_bar: ProgressBar
var feedback_fog: ColorRect
var labs_overlay: Control
var labs_panel: PanelContainer
var labs_content: VBoxContainer
var media_requests: Array[HTTPRequest] = []
var red_fog_time := 0.0
var green_fog_time := 0.0
var correct_advance_delay := 0.0
@@ -151,6 +153,12 @@ func _ready() -> void:
stem_label.custom_minimum_size = Vector2(0, 96)
question_content.add_child(stem_label)
media_container = VBoxContainer.new()
media_container.visible = false
media_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL
media_container.add_theme_constant_override("separation", 10)
question_content.add_child(media_container)
lead_label = Label.new()
lead_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
Style.set_label(lead_label, 21, Style.TEXT)
@@ -336,6 +344,7 @@ func _load_question() -> void:
_update_timer_bar()
stem_plain_text = str(question.get("stem", ""))
_render_stem()
_load_question_media(question)
lead_label.text = question.get("lead_in", "")
_refresh_labs_button()
@@ -471,8 +480,6 @@ func _submit_or_next() -> void:
current_answer = {}
next_button.text = "Submit"
next_button.disabled = true
if skip_button != null:
skip_button.disabled = true
_trigger_wrong_feedback()
_apply_saved_choice_state()
return
@@ -697,6 +704,112 @@ func _current_question_has_labs() -> bool:
return not labs.is_empty()
func _load_question_media(question: Dictionary) -> void:
_clear_question_media()
var media: Array = question.get("media", [])
if media.is_empty():
return
media_container.visible = true
var question_id := str(question.get("id", ""))
for item in media:
var media_url := _resolve_media_url(str(item))
if media_url == "":
continue
var frame := PanelContainer.new()
frame.size_flags_horizontal = Control.SIZE_EXPAND_FILL
frame.add_theme_stylebox_override("panel", Style.panel_box(Color("111a2a"), Color("344463")))
media_container.add_child(frame)
var image_box := VBoxContainer.new()
image_box.add_theme_constant_override("separation", 8)
frame.add_child(image_box)
var status := Label.new()
status.text = "Loading image..."
status.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
Style.set_label(status, 15, Style.MUTED)
image_box.add_child(status)
var texture_rect := TextureRect.new()
texture_rect.visible = false
texture_rect.expand_mode = TextureRect.EXPAND_FIT_WIDTH
texture_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
texture_rect.custom_minimum_size = Vector2(0, 260)
texture_rect.size_flags_horizontal = Control.SIZE_EXPAND_FILL
image_box.add_child(texture_rect)
var request := HTTPRequest.new()
request.request_completed.connect(
func(result: int, response_code: int, _headers: PackedStringArray, body: PackedByteArray):
_on_media_loaded(result, response_code, body, question_id, texture_rect, status)
)
media_requests.append(request)
add_child(request)
var err := request.request(media_url)
if err != OK:
status.text = "Image unavailable."
func _clear_question_media() -> void:
for request in media_requests:
if is_instance_valid(request):
request.queue_free()
media_requests.clear()
if media_container == null:
return
for child in media_container.get_children():
media_container.remove_child(child)
child.queue_free()
media_container.visible = false
func _resolve_media_url(value: String) -> String:
var media_url := value.strip_edges()
if media_url == "":
return ""
if media_url.begins_with("http://") or media_url.begins_with("https://"):
return media_url
if not media_url.begins_with("/"):
media_url = "/%s" % media_url
var base_url := QuestionBankService.question_api_base_url
var scheme_end := base_url.find("://")
if scheme_end < 0:
return media_url
var path_start := base_url.find("/", scheme_end + 3)
var origin := base_url if path_start < 0 else base_url.substr(0, path_start)
return "%s%s" % [origin, media_url]
func _on_media_loaded(
result: int,
response_code: int,
body: PackedByteArray,
question_id: String,
texture_rect: TextureRect,
status: Label) -> void:
if str(BlockSessionService.get_current_question().get("id", "")) != question_id:
return
if result != HTTPRequest.RESULT_SUCCESS or response_code < 200 or response_code >= 300:
status.text = "Image unavailable."
return
var image := Image.new()
var err := image.load_png_from_buffer(body)
if err != OK:
err = image.load_jpg_from_buffer(body)
if err != OK:
err = image.load_webp_from_buffer(body)
if err != OK:
status.text = "Image unavailable."
return
texture_rect.texture = ImageTexture.create_from_image(image)
texture_rect.visible = true
status.visible = false
func _build_labs_modal() -> void:
labs_overlay = Control.new()
labs_overlay.visible = false