Add CCS study tools and case reporting
This commit is contained in:
+74
-24
@@ -3,36 +3,86 @@ extends VBoxContainer
|
||||
const Style := preload("res://scripts/ui/ui_style.gd")
|
||||
var main: Control
|
||||
var title: Label
|
||||
var body: RichTextLabel
|
||||
var score_body: RichTextLabel
|
||||
var timeline_body: RichTextLabel
|
||||
var references: VBoxContainer
|
||||
var report_dialog: Control
|
||||
var report_button: Button
|
||||
var current_attempt := {}
|
||||
|
||||
func _ready() -> void:
|
||||
set_anchors_preset(Control.PRESET_FULL_RECT); add_theme_constant_override("separation", 12)
|
||||
title = Label.new(); Style.set_label(title, 32, Style.TEXT); add_child(title)
|
||||
body = RichTextLabel.new(); body.bbcode_enabled = true; body.fit_content = false; body.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
body.meta_clicked.connect(func(meta): OS.shell_open(str(meta))); add_child(body)
|
||||
CCSService.attempt_changed.connect(_refresh_loaded_attempt)
|
||||
set_anchors_preset(Control.PRESET_FULL_RECT); add_theme_constant_override("separation", 10)
|
||||
var toolbar := HBoxContainer.new(); toolbar.add_theme_constant_override("separation", 8); add_child(toolbar)
|
||||
title = Label.new(); title.size_flags_horizontal = Control.SIZE_EXPAND_FILL; Style.set_label(title, 30, Style.TEXT); toolbar.add_child(title)
|
||||
var smaller := Button.new(); smaller.text = "A-"; smaller.tooltip_text = "Decrease text size"; smaller.pressed.connect(_zoom.bind(-CCSService.TEXT_SCALE_STEP)); toolbar.add_child(smaller)
|
||||
var larger := Button.new(); larger.text = "A+"; larger.tooltip_text = "Increase text size"; larger.pressed.connect(_zoom.bind(CCSService.TEXT_SCALE_STEP)); toolbar.add_child(larger)
|
||||
var clear := Button.new(); clear.text = "Clear"; clear.tooltip_text = "Clear highlights"; clear.pressed.connect(_clear_highlights); toolbar.add_child(clear)
|
||||
report_button = Button.new(); report_button.text = "Report"; report_button.tooltip_text = "Report CCS case"; report_button.pressed.connect(_report); toolbar.add_child(report_button)
|
||||
var scroll := ScrollContainer.new(); scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL; add_child(scroll)
|
||||
var content := VBoxContainer.new(); content.size_flags_horizontal = Control.SIZE_EXPAND_FILL; content.add_theme_constant_override("separation", 14); scroll.add_child(content)
|
||||
score_body = _study_label("score"); score_body.custom_minimum_size.y = 200; content.add_child(score_body)
|
||||
timeline_body = _study_label("timeline"); timeline_body.custom_minimum_size.y = 260; content.add_child(timeline_body)
|
||||
references = VBoxContainer.new(); content.add_child(references)
|
||||
CCSService.attempt_changed.connect(_refresh_loaded_attempt); CCSService.report_submitted.connect(_report_submitted)
|
||||
report_dialog = preload("res://scripts/ui/ccs_report_dialog.gd").new(); add_child(report_dialog)
|
||||
var row := HBoxContainer.new(); add_child(row)
|
||||
var cases := Button.new(); cases.text = "More cases"; Style.style_primary_button(cases); cases.pressed.connect(func(): main.show_screen("ccs_hub")); row.add_child(cases)
|
||||
var history := Button.new(); history.text = "Case history"; history.pressed.connect(func(): main.show_screen("ccs_history")); row.add_child(history)
|
||||
|
||||
func on_show(payload: Variant = null) -> void:
|
||||
if typeof(payload) == TYPE_STRING:
|
||||
CCSService.load_attempt(str(payload))
|
||||
return
|
||||
var attempt: Dictionary = payload if typeof(payload) == TYPE_DICTIONARY else CCSService.attempt
|
||||
if attempt.is_empty(): return
|
||||
var score: Dictionary = attempt.get("score", {})
|
||||
title.text = "%s: %d%%" % [attempt.get("caseTitle", "Case review"), int(round(float(score.get("percent", 0))))]
|
||||
var text := "[b]Scoring review[/b]\n"
|
||||
for criterion in score.get("criteria", []):
|
||||
var status := str(criterion.get("status", "missed"))
|
||||
text += "\n[color=%s][b]%s %s%d[/b][/color]\n%s\n" % [Style.LIME.to_html() if status in ["credited", "avoided"] else Style.RED.to_html(), status.capitalize(), "+" if int(criterion.get("points", 0)) >= 0 else "", criterion.get("points", 0), criterion.get("rationale", "")]
|
||||
text += "\n[b]Case timeline[/b]\n"
|
||||
for entry in attempt.get("chart", []): text += "\nT+%d [%s] %s" % [entry.get("simulatedMinute", 0), entry.get("category", "note"), entry.get("text", "")]
|
||||
if not attempt.get("references", []).is_empty():
|
||||
text += "\n\n[b]References[/b]\n"
|
||||
for reference in attempt.get("references", []): text += "\n[url=%s]%s[/url]" % [reference.get("url", ""), reference.get("title", reference.get("url", "Reference"))]
|
||||
body.text = text
|
||||
func _study_label(region: String) -> RichTextLabel:
|
||||
var label := RichTextLabel.new(); label.bbcode_enabled = true; label.fit_content = true; label.selection_enabled = true; label.set_meta("ccs_region", region); label.gui_input.connect(_text_input.bind(label)); return label
|
||||
|
||||
func on_show(payload: Variant = null) -> void:
|
||||
if typeof(payload) == TYPE_STRING: CCSService.load_attempt(str(payload)); return
|
||||
current_attempt = payload if typeof(payload) == TYPE_DICTIONARY else CCSService.attempt
|
||||
if current_attempt.is_empty(): return
|
||||
var score: Dictionary = current_attempt.get("score", {})
|
||||
title.text = "%s: %d%%" % [current_attempt.get("caseTitle", "Case review"), int(round(float(score.get("percent", 0))))]
|
||||
_render_text(); _render_references(); _apply_text_scale()
|
||||
|
||||
func _score_plain() -> String:
|
||||
var lines: Array[String] = ["Scoring review"]
|
||||
for criterion in current_attempt.get("score", {}).get("criteria", []):
|
||||
lines.append("%s %s%d\n%s" % [str(criterion.get("status", "missed")).capitalize(), "+" if int(criterion.get("points", 0)) >= 0 else "", criterion.get("points", 0), criterion.get("rationale", "")])
|
||||
return "\n\n".join(lines)
|
||||
|
||||
func _timeline_plain() -> String:
|
||||
var lines: Array[String] = ["Case timeline"]
|
||||
for entry in current_attempt.get("chart", []): lines.append("T+%d [%s] %s" % [entry.get("simulatedMinute", 0), entry.get("category", "note"), entry.get("text", "")])
|
||||
return "\n\n".join(lines)
|
||||
|
||||
func _render_text() -> void:
|
||||
var id := str(current_attempt.get("id", ""))
|
||||
score_body.text = CCSService.highlighted_text(id, "score", _score_plain())
|
||||
timeline_body.text = CCSService.highlighted_text(id, "timeline", _timeline_plain())
|
||||
|
||||
func _render_references() -> void:
|
||||
for child in references.get_children(): child.queue_free()
|
||||
if current_attempt.get("references", []).is_empty(): return
|
||||
var heading := Label.new(); heading.text = "References"; Style.set_label(heading, int(round(19.0 * CCSService.text_scale)), Style.TEXT); references.add_child(heading)
|
||||
for reference in current_attempt.get("references", []):
|
||||
var link := LinkButton.new(); link.text = str(reference.get("title", reference.get("url", "Reference"))); link.pressed.connect(OS.shell_open.bind(str(reference.get("url", "")))); link.add_theme_font_size_override("font_size", int(round(16.0 * CCSService.text_scale))); references.add_child(link)
|
||||
|
||||
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:
|
||||
if label.get_selected_text().strip_edges().is_empty(): return
|
||||
var start := label.get_selection_from(); var end := label.get_selection_to()
|
||||
CCSService.toggle_highlight(str(current_attempt.get("id", "")), str(label.get_meta("ccs_region", "")), mini(start,end), maxi(start,end)); label.deselect(); _render_text()
|
||||
|
||||
func _zoom(delta: float) -> void:
|
||||
CCSService.adjust_text_scale(delta); _apply_text_scale(); _render_references()
|
||||
|
||||
func _apply_text_scale() -> void:
|
||||
var size := int(round(18.0 * CCSService.text_scale))
|
||||
for label in [score_body, timeline_body]: label.add_theme_font_size_override("normal_font_size", size); label.add_theme_font_size_override("bold_font_size", size)
|
||||
|
||||
func _clear_highlights() -> void:
|
||||
CCSService.clear_highlights(str(current_attempt.get("id", ""))); _render_text()
|
||||
|
||||
func _report() -> void: report_dialog.show_for(str(current_attempt.get("id", "")))
|
||||
func _report_submitted(_id: String) -> void: Style.style_chip(report_button, Style.RED); report_button.tooltip_text = "Report submitted; report another issue"
|
||||
func _refresh_loaded_attempt() -> void:
|
||||
if visible and str(CCSService.attempt.get("state", "")) == "completed": on_show(CCSService.attempt)
|
||||
|
||||
Reference in New Issue
Block a user