Fix dashboard info-icon rendering and profile <null> display
Info badge: the icon was a PanelContainer using StyleBoxFlat corner radius to fake a circle, with the radius exceeding half the box size, causing visible artifacts. Replaced with a custom-drawn Control (draw_arc) for a clean ring at any size, and added a hover tooltip explaining what each stat card measures. Profile: Dictionary.get(key, default) only falls back when the key is missing, not when it's present with a null value, so a null displayName rendered as the literal string "<null>" (str(null) in GDScript) - including pre-filling the edit-profile dialog with it. Added a shared string_field() helper that treats null the same as missing, applied everywhere an account/preference string field is displayed or edited. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
@@ -5,6 +5,7 @@ const TrendChart := preload("res://scripts/ui/dashboard_chart.gd")
|
||||
const ActivityChart := preload("res://scripts/ui/dashboard_activity_chart.gd")
|
||||
const DashboardSparkline := preload("res://scripts/ui/dashboard_sparkline.gd")
|
||||
const DashboardSegmentBar := preload("res://scripts/ui/dashboard_segment_bar.gd")
|
||||
const DashboardInfoBadge := preload("res://scripts/ui/dashboard_info_badge.gd")
|
||||
|
||||
var main: Control
|
||||
var scroll: ScrollContainer
|
||||
@@ -82,15 +83,18 @@ func _build() -> void:
|
||||
|
||||
qbank_sparkline = DashboardSparkline.new()
|
||||
qbank_sparkline.custom_minimum_size = Vector2(0, 34)
|
||||
_add_stat_card("qbank", "Question Bank Rating", "book-open", Brand.ROYAL_BLUE, qbank_sparkline)
|
||||
_add_stat_card("qbank", "Question Bank Rating", "book-open", Brand.ROYAL_BLUE, qbank_sparkline,
|
||||
"Your Elo-style rating from Question Bank answers, weighted by each question's difficulty.")
|
||||
|
||||
ccs_sparkline = DashboardSparkline.new()
|
||||
ccs_sparkline.custom_minimum_size = Vector2(0, 34)
|
||||
_add_stat_card("ccs", "CCS Rating", "stethoscope", Brand.SUCCESS, ccs_sparkline)
|
||||
_add_stat_card("ccs", "CCS Rating", "stethoscope", Brand.SUCCESS, ccs_sparkline,
|
||||
"Your Elo-style rating from timed CCS case performance, weighted by each case's difficulty.")
|
||||
|
||||
readiness_segment_bar = DashboardSegmentBar.new()
|
||||
readiness_segment_bar.custom_minimum_size = Vector2(0, 10)
|
||||
_add_stat_card("readiness", "Exam Readiness", "chart-no-axes-column-increasing", Brand.PURPLE, readiness_segment_bar)
|
||||
_add_stat_card("readiness", "Exam Readiness", "chart-no-axes-column-increasing", Brand.PURPLE, readiness_segment_bar,
|
||||
"Predicted pass probability from your most recent completed exam simulation.")
|
||||
|
||||
accuracy_progress_bar = ProgressBar.new()
|
||||
accuracy_progress_bar.show_percentage = false
|
||||
@@ -98,7 +102,8 @@ func _build() -> void:
|
||||
accuracy_progress_bar.max_value = 100.0
|
||||
accuracy_progress_bar.custom_minimum_size = Vector2(0, 10)
|
||||
_style_progress(accuracy_progress_bar, Brand.AMBER)
|
||||
_add_stat_card("accuracy", "Average Score", "star", Brand.AMBER, accuracy_progress_bar)
|
||||
_add_stat_card("accuracy", "Average Score", "star", Brand.AMBER, accuracy_progress_bar,
|
||||
"Your first-attempt accuracy across your most recent 100 answered questions.")
|
||||
|
||||
analytics_grid = GridContainer.new()
|
||||
analytics_grid.columns = 2
|
||||
@@ -415,28 +420,23 @@ func _render_recent(items: Array) -> void:
|
||||
recent_list.add_child(row)
|
||||
|
||||
|
||||
func _info_badge(dark: bool) -> Control:
|
||||
var badge := PanelContainer.new()
|
||||
badge.custom_minimum_size = Vector2(15, 15)
|
||||
func _info_badge(dark: bool, tooltip: String) -> Control:
|
||||
var badge := DashboardInfoBadge.new()
|
||||
badge.custom_minimum_size = Vector2(16, 16)
|
||||
badge.size_flags_vertical = Control.SIZE_SHRINK_BEGIN
|
||||
var box := StyleBoxFlat.new()
|
||||
box.bg_color = Color.TRANSPARENT
|
||||
box.border_color = Brand.muted_color(dark)
|
||||
box.set_border_width_all(1)
|
||||
box.set_corner_radius_all(8)
|
||||
box.content_margin_left = 0
|
||||
box.content_margin_right = 0
|
||||
box.content_margin_top = 0
|
||||
box.content_margin_bottom = 0
|
||||
badge.add_theme_stylebox_override("panel", box)
|
||||
badge.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
badge.tooltip_text = tooltip
|
||||
badge.set_ring_color(Brand.muted_color(dark))
|
||||
var glyph := _label("i", 10, Brand.muted_color(dark), true)
|
||||
glyph.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
glyph.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
glyph.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
glyph.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
badge.add_child(glyph)
|
||||
return badge
|
||||
|
||||
|
||||
func _add_stat_card(key: String, title: String, icon_name: String, accent: Color, bottom_visual: Control) -> void:
|
||||
func _add_stat_card(key: String, title: String, icon_name: String, accent: Color, bottom_visual: Control, info_tooltip: String) -> void:
|
||||
var dark := UIThemeService.is_dark_mode()
|
||||
var card_content := VBoxContainer.new()
|
||||
card_content.custom_minimum_size = Vector2(220, 0)
|
||||
@@ -461,7 +461,7 @@ func _add_stat_card(key: String, title: String, icon_name: String, accent: Color
|
||||
title_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
title_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
header.add_child(title_label)
|
||||
header.add_child(_info_badge(dark))
|
||||
header.add_child(_info_badge(dark, info_tooltip))
|
||||
|
||||
var value := _label("--", 28, Brand.text_color(dark), true)
|
||||
card_content.add_child(value)
|
||||
|
||||
Reference in New Issue
Block a user