Add lab results modal
This commit is contained in:
@@ -34,12 +34,16 @@ var explanation_label: RichTextLabel
|
|||||||
var question_elo_label: Label
|
var question_elo_label: Label
|
||||||
var next_button: Button
|
var next_button: Button
|
||||||
var skip_button: Button
|
var skip_button: Button
|
||||||
|
var labs_button: Button
|
||||||
var flag_button: Button
|
var flag_button: Button
|
||||||
var report_button: Button
|
var report_button: Button
|
||||||
var nav_palette: HBoxContainer
|
var nav_palette: HBoxContainer
|
||||||
var question_content: VBoxContainer
|
var question_content: VBoxContainer
|
||||||
var timer_bar: ProgressBar
|
var timer_bar: ProgressBar
|
||||||
var feedback_fog: ColorRect
|
var feedback_fog: ColorRect
|
||||||
|
var labs_overlay: Control
|
||||||
|
var labs_panel: PanelContainer
|
||||||
|
var labs_content: VBoxContainer
|
||||||
var red_fog_time := 0.0
|
var red_fog_time := 0.0
|
||||||
var green_fog_time := 0.0
|
var green_fog_time := 0.0
|
||||||
var correct_advance_delay := 0.0
|
var correct_advance_delay := 0.0
|
||||||
@@ -77,6 +81,14 @@ func _ready() -> void:
|
|||||||
toolbar.add_theme_constant_override("separation", 8)
|
toolbar.add_theme_constant_override("separation", 8)
|
||||||
toolbar.alignment = BoxContainer.ALIGNMENT_END
|
toolbar.alignment = BoxContainer.ALIGNMENT_END
|
||||||
|
|
||||||
|
labs_button = Button.new()
|
||||||
|
labs_button.tooltip_text = "Review lab results"
|
||||||
|
labs_button.icon = _make_labs_icon()
|
||||||
|
labs_button.custom_minimum_size = Vector2(44, 44)
|
||||||
|
Style.style_chip(labs_button, Style.LIME)
|
||||||
|
labs_button.pressed.connect(_show_labs_modal)
|
||||||
|
toolbar.add_child(labs_button)
|
||||||
|
|
||||||
var clear_highlight_button := Button.new()
|
var clear_highlight_button := Button.new()
|
||||||
clear_highlight_button.tooltip_text = "Clear highlights"
|
clear_highlight_button.tooltip_text = "Clear highlights"
|
||||||
clear_highlight_button.icon = _make_clear_highlights_icon()
|
clear_highlight_button.icon = _make_clear_highlights_icon()
|
||||||
@@ -254,6 +266,8 @@ func _ready() -> void:
|
|||||||
feedback_fog.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
feedback_fog.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||||
add_child(feedback_fog)
|
add_child(feedback_fog)
|
||||||
|
|
||||||
|
_build_labs_modal()
|
||||||
|
|
||||||
|
|
||||||
func on_show(_payload: Variant = null) -> void:
|
func on_show(_payload: Variant = null) -> void:
|
||||||
_load_question()
|
_load_question()
|
||||||
@@ -273,6 +287,10 @@ func _process(delta: float) -> void:
|
|||||||
|
|
||||||
|
|
||||||
func _input(event: InputEvent) -> void:
|
func _input(event: InputEvent) -> void:
|
||||||
|
if labs_overlay != null and labs_overlay.visible and event.is_action_pressed("ui_cancel"):
|
||||||
|
_hide_labs_modal()
|
||||||
|
get_viewport().set_input_as_handled()
|
||||||
|
return
|
||||||
if not visible or pending_correct_advance or stem_label == null:
|
if not visible or pending_correct_advance or stem_label == null:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -289,6 +307,7 @@ func _input(event: InputEvent) -> void:
|
|||||||
|
|
||||||
func _load_question() -> void:
|
func _load_question() -> void:
|
||||||
_reset_wrong_feedback()
|
_reset_wrong_feedback()
|
||||||
|
_hide_labs_modal()
|
||||||
eliminated.clear()
|
eliminated.clear()
|
||||||
locked_wrong_choice_ids.clear()
|
locked_wrong_choice_ids.clear()
|
||||||
_clear_followups()
|
_clear_followups()
|
||||||
@@ -318,6 +337,7 @@ func _load_question() -> void:
|
|||||||
stem_plain_text = str(question.get("stem", ""))
|
stem_plain_text = str(question.get("stem", ""))
|
||||||
_render_stem()
|
_render_stem()
|
||||||
lead_label.text = question.get("lead_in", "")
|
lead_label.text = question.get("lead_in", "")
|
||||||
|
_refresh_labs_button()
|
||||||
|
|
||||||
var choices: Array = question.get("choices", [])
|
var choices: Array = question.get("choices", [])
|
||||||
for index in range(choice_buttons.size()):
|
for index in range(choice_buttons.size()):
|
||||||
@@ -655,6 +675,7 @@ func _toggle_report_current_question() -> void:
|
|||||||
|
|
||||||
|
|
||||||
func _refresh_toolbar_state() -> void:
|
func _refresh_toolbar_state() -> void:
|
||||||
|
_refresh_labs_button()
|
||||||
if flag_button != null:
|
if flag_button != null:
|
||||||
Style.style_chip(flag_button, Style.AMBER if flagged else Style.CYAN)
|
Style.style_chip(flag_button, Style.AMBER if flagged else Style.CYAN)
|
||||||
if report_button != null:
|
if report_button != null:
|
||||||
@@ -662,6 +683,152 @@ func _refresh_toolbar_state() -> void:
|
|||||||
Style.style_chip(report_button, Style.RED if reported_question_ids.has(question_id) else Style.CYAN)
|
Style.style_chip(report_button, Style.RED if reported_question_ids.has(question_id) else Style.CYAN)
|
||||||
|
|
||||||
|
|
||||||
|
func _refresh_labs_button() -> void:
|
||||||
|
if labs_button == null:
|
||||||
|
return
|
||||||
|
labs_button.visible = _current_question_has_labs()
|
||||||
|
|
||||||
|
|
||||||
|
func _current_question_has_labs() -> bool:
|
||||||
|
var question := BlockSessionService.get_current_question()
|
||||||
|
if question.is_empty():
|
||||||
|
return false
|
||||||
|
var labs: Array = question.get("labs", [])
|
||||||
|
return not labs.is_empty()
|
||||||
|
|
||||||
|
|
||||||
|
func _build_labs_modal() -> void:
|
||||||
|
labs_overlay = Control.new()
|
||||||
|
labs_overlay.visible = false
|
||||||
|
labs_overlay.top_level = true
|
||||||
|
labs_overlay.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||||
|
labs_overlay.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||||
|
labs_overlay.gui_input.connect(_labs_overlay_gui_input)
|
||||||
|
add_child(labs_overlay)
|
||||||
|
|
||||||
|
var dim := ColorRect.new()
|
||||||
|
dim.color = Color(0.02, 0.03, 0.05, 0.72)
|
||||||
|
dim.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
dim.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||||
|
labs_overlay.add_child(dim)
|
||||||
|
|
||||||
|
var center := CenterContainer.new()
|
||||||
|
center.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
center.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||||
|
labs_overlay.add_child(center)
|
||||||
|
|
||||||
|
labs_panel = PanelContainer.new()
|
||||||
|
labs_panel.custom_minimum_size = Vector2(760, 430)
|
||||||
|
labs_panel.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||||
|
labs_panel.add_theme_stylebox_override("panel", Style.panel_box(Color("172337"), Style.LIME))
|
||||||
|
center.add_child(labs_panel)
|
||||||
|
|
||||||
|
var modal_body := VBoxContainer.new()
|
||||||
|
modal_body.add_theme_constant_override("separation", 14)
|
||||||
|
labs_panel.add_child(modal_body)
|
||||||
|
|
||||||
|
var header := HBoxContainer.new()
|
||||||
|
header.add_theme_constant_override("separation", 10)
|
||||||
|
modal_body.add_child(header)
|
||||||
|
|
||||||
|
var title := Label.new()
|
||||||
|
title.text = "Lab Results"
|
||||||
|
title.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
|
Style.set_label(title, 26, Style.TEXT)
|
||||||
|
header.add_child(title)
|
||||||
|
|
||||||
|
var close_button := Button.new()
|
||||||
|
close_button.text = "X"
|
||||||
|
close_button.tooltip_text = "Close lab results"
|
||||||
|
close_button.custom_minimum_size = Vector2(42, 38)
|
||||||
|
Style.style_chip(close_button, Style.RED)
|
||||||
|
close_button.pressed.connect(_hide_labs_modal)
|
||||||
|
header.add_child(close_button)
|
||||||
|
|
||||||
|
var scroll := ScrollContainer.new()
|
||||||
|
scroll.custom_minimum_size = Vector2(0, 320)
|
||||||
|
scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||||
|
scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||||||
|
modal_body.add_child(scroll)
|
||||||
|
|
||||||
|
labs_content = VBoxContainer.new()
|
||||||
|
labs_content.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
|
labs_content.add_theme_constant_override("separation", 8)
|
||||||
|
scroll.add_child(labs_content)
|
||||||
|
|
||||||
|
|
||||||
|
func _labs_overlay_gui_input(event: InputEvent) -> void:
|
||||||
|
var mouse_event := event as InputEventMouseButton
|
||||||
|
if mouse_event == null or not mouse_event.pressed or mouse_event.button_index != MOUSE_BUTTON_LEFT:
|
||||||
|
return
|
||||||
|
if labs_panel == null or not labs_panel.get_global_rect().has_point(mouse_event.global_position):
|
||||||
|
_hide_labs_modal()
|
||||||
|
get_viewport().set_input_as_handled()
|
||||||
|
|
||||||
|
|
||||||
|
func _show_labs_modal() -> void:
|
||||||
|
if labs_overlay == null or labs_content == null:
|
||||||
|
return
|
||||||
|
var question := BlockSessionService.get_current_question()
|
||||||
|
var labs: Array = question.get("labs", [])
|
||||||
|
if labs.is_empty():
|
||||||
|
return
|
||||||
|
_populate_labs_modal(labs)
|
||||||
|
labs_overlay.visible = true
|
||||||
|
labs_overlay.move_to_front()
|
||||||
|
|
||||||
|
|
||||||
|
func _hide_labs_modal() -> void:
|
||||||
|
if labs_overlay != null:
|
||||||
|
labs_overlay.visible = false
|
||||||
|
|
||||||
|
|
||||||
|
func _populate_labs_modal(labs: Array) -> void:
|
||||||
|
for child in labs_content.get_children():
|
||||||
|
labs_content.remove_child(child)
|
||||||
|
child.queue_free()
|
||||||
|
|
||||||
|
var grid := GridContainer.new()
|
||||||
|
grid.columns = 3
|
||||||
|
grid.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
|
grid.add_theme_constant_override("h_separation", 16)
|
||||||
|
grid.add_theme_constant_override("v_separation", 8)
|
||||||
|
labs_content.add_child(grid)
|
||||||
|
|
||||||
|
_add_lab_table_label(grid, "Test", Style.CYAN, true)
|
||||||
|
_add_lab_table_label(grid, "Value", Style.CYAN, true)
|
||||||
|
_add_lab_table_label(grid, "Reference", Style.CYAN, true)
|
||||||
|
|
||||||
|
for lab in labs:
|
||||||
|
if typeof(lab) != TYPE_DICTIONARY:
|
||||||
|
continue
|
||||||
|
var lab_dict: Dictionary = lab
|
||||||
|
_add_lab_table_label(grid, str(lab_dict.get("name", "-")), Style.TEXT)
|
||||||
|
_add_lab_table_label(grid, _format_lab_value(lab_dict), Style.TEXT)
|
||||||
|
_add_lab_table_label(grid, str(lab_dict.get("reference", "-")), Style.MUTED)
|
||||||
|
|
||||||
|
|
||||||
|
func _add_lab_table_label(parent: GridContainer, text: String, color: Color, is_header: bool = false) -> void:
|
||||||
|
var label := Label.new()
|
||||||
|
label.text = text if text.strip_edges() != "" else "-"
|
||||||
|
label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||||
|
label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
|
Style.set_label(label, 16 if not is_header else 17, color)
|
||||||
|
parent.add_child(label)
|
||||||
|
|
||||||
|
|
||||||
|
func _format_lab_value(lab: Dictionary) -> String:
|
||||||
|
var value := str(lab.get("value", "")).strip_edges()
|
||||||
|
var unit := str(lab.get("unit", "")).strip_edges()
|
||||||
|
if value == "" and unit == "":
|
||||||
|
return "-"
|
||||||
|
if unit == "":
|
||||||
|
return value
|
||||||
|
if value == "":
|
||||||
|
return unit
|
||||||
|
return "%s %s" % [value, unit]
|
||||||
|
|
||||||
|
|
||||||
func _render_stem() -> void:
|
func _render_stem() -> void:
|
||||||
var question_id := str(BlockSessionService.get_current_question().get("id", ""))
|
var question_id := str(BlockSessionService.get_current_question().get("id", ""))
|
||||||
var ranges: Array = question_highlights.get(question_id, []).duplicate()
|
var ranges: Array = question_highlights.get(question_id, []).duplicate()
|
||||||
@@ -1137,6 +1304,33 @@ func _strike_text(value: String) -> String:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
func _make_labs_icon() -> Texture2D:
|
||||||
|
var image := Image.create(24, 24, false, Image.FORMAT_RGBA8)
|
||||||
|
image.fill(Color(0, 0, 0, 0))
|
||||||
|
var glass_color := Style.TEXT
|
||||||
|
var fluid_color := Style.LIME
|
||||||
|
for x in range(8, 16):
|
||||||
|
image.set_pixel(x, 4, glass_color)
|
||||||
|
for y in range(5, 10):
|
||||||
|
image.set_pixel(10, y, glass_color)
|
||||||
|
image.set_pixel(13, y, glass_color)
|
||||||
|
for y in range(10, 20):
|
||||||
|
var left := 10 - int(floor(float(y - 10) / 3.0))
|
||||||
|
var right := 13 + int(floor(float(y - 10) / 3.0))
|
||||||
|
image.set_pixel(left, y, glass_color)
|
||||||
|
image.set_pixel(right, y, glass_color)
|
||||||
|
for x in range(7, 17):
|
||||||
|
image.set_pixel(x, 20, glass_color)
|
||||||
|
for y in range(15, 20):
|
||||||
|
for x in range(8, 16):
|
||||||
|
if x >= 10 - int(floor(float(y - 10) / 3.0)) and x <= 13 + int(floor(float(y - 10) / 3.0)):
|
||||||
|
image.set_pixel(x, y, fluid_color)
|
||||||
|
image.set_pixel(7, 7, Style.CYAN)
|
||||||
|
image.set_pixel(17, 9, Style.CYAN)
|
||||||
|
image.set_pixel(18, 6, Style.CYAN)
|
||||||
|
return ImageTexture.create_from_image(image)
|
||||||
|
|
||||||
|
|
||||||
func _make_flag_icon() -> Texture2D:
|
func _make_flag_icon() -> Texture2D:
|
||||||
var image := Image.create(24, 24, false, Image.FORMAT_RGBA8)
|
var image := Image.create(24, 24, false, Image.FORMAT_RGBA8)
|
||||||
image.fill(Color(0, 0, 0, 0))
|
image.fill(Color(0, 0, 0, 0))
|
||||||
|
|||||||
Reference in New Issue
Block a user