diff --git a/assets/icons/medmind/badge-circle-filled.svg b/assets/icons/medmind/badge-circle-filled.svg
new file mode 100644
index 0000000..5661376
--- /dev/null
+++ b/assets/icons/medmind/badge-circle-filled.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/medmind/badge-circle-filled.svg.import b/assets/icons/medmind/badge-circle-filled.svg.import
new file mode 100644
index 0000000..d50f21c
--- /dev/null
+++ b/assets/icons/medmind/badge-circle-filled.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://neyyjsv1yi2y"
+path="res://.godot/imported/badge-circle-filled.svg-c615dfa6e3ba91563385092d88757ba4.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://assets/icons/medmind/badge-circle-filled.svg"
+dest_files=["res://.godot/imported/badge-circle-filled.svg-c615dfa6e3ba91563385092d88757ba4.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/assets/icons/medmind/badge-circle-outline.svg b/assets/icons/medmind/badge-circle-outline.svg
new file mode 100644
index 0000000..5c299dc
--- /dev/null
+++ b/assets/icons/medmind/badge-circle-outline.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/medmind/badge-circle-outline.svg.import b/assets/icons/medmind/badge-circle-outline.svg.import
new file mode 100644
index 0000000..3edb651
--- /dev/null
+++ b/assets/icons/medmind/badge-circle-outline.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://2epi5x1l2k3k"
+path="res://.godot/imported/badge-circle-outline.svg-48e5795ac9a851c7826f85026138d065.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://assets/icons/medmind/badge-circle-outline.svg"
+dest_files=["res://.godot/imported/badge-circle-outline.svg-48e5795ac9a851c7826f85026138d065.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/scripts/ui/question_player.gd b/scripts/ui/question_player.gd
index 1ca107a..96eeecc 100644
--- a/scripts/ui/question_player.gd
+++ b/scripts/ui/question_player.gd
@@ -9,6 +9,8 @@ const LABS_DARK_ICON := preload("res://assets/icons/medmind/labs-dark.svg")
const LABS_LIGHT_ICON := preload("res://assets/icons/medmind/labs-light.svg")
const REPORT_QUESTION_DARK_ICON := preload("res://assets/icons/medmind/report-question-dark.svg")
const REPORT_QUESTION_LIGHT_ICON := preload("res://assets/icons/medmind/report-question-light.svg")
+const BADGE_CIRCLE_OUTLINE_ICON := preload("res://assets/icons/medmind/badge-circle-outline.svg")
+const BADGE_CIRCLE_FILLED_ICON := preload("res://assets/icons/medmind/badge-circle-filled.svg")
const FEEDBACK_FADE_SECONDS := 0.45
const CORRECT_ADVANCE_DELAY_SECONDS := 1.25
const TEXT_SCALE_MIN := 0.85
@@ -104,6 +106,7 @@ var question_content: VBoxContainer
var media_container: VBoxContainer
var timer_bar: ProgressBar
var choice_badges: Array[Label] = []
+var choice_badge_textures: Array[TextureRect] = []
var feedback_fog: ColorRect
var labs_overlay: Control
var labs_panel: PanelContainer
@@ -424,7 +427,7 @@ func _ready() -> void:
question_content.add_child(question_elo_label)
var choices_container := VBoxContainer.new()
- choices_container.add_theme_constant_override("separation", 8)
+ choices_container.add_theme_constant_override("separation", 10)
question_content.add_child(choices_container)
for index in range(5):
@@ -434,7 +437,7 @@ func _ready() -> void:
choice_rows.append(choice_row)
var button := Button.new()
- button.custom_minimum_size = Vector2(0, 54)
+ button.custom_minimum_size = Vector2(0, 68)
button.alignment = HORIZONTAL_ALIGNMENT_LEFT
Style.style_answer_button(button)
UISoundService.set_button_sound(button, "select")
@@ -443,20 +446,36 @@ func _ready() -> void:
button.gui_input.connect(func(event: InputEvent, i := index): _choice_gui_input(event, i))
choice_buttons.append(button)
+ var badge_wrap := Control.new()
+ badge_wrap.mouse_filter = Control.MOUSE_FILTER_IGNORE
+ badge_wrap.anchor_left = 0.0
+ badge_wrap.anchor_top = 0.0
+ badge_wrap.anchor_right = 0.0
+ badge_wrap.anchor_bottom = 0.0
+ badge_wrap.offset_left = 16
+ badge_wrap.offset_top = 17
+ badge_wrap.offset_right = 50
+ badge_wrap.offset_bottom = 51
+ button.add_child(badge_wrap)
+
+ # The badge circle is a pre-rendered SVG (tinted per-state via modulate) rather than a
+ # StyleBoxFlat corner-radius circle - a perfectly circular StyleBoxFlat is a known Godot
+ # degenerate case that can show a seam/faceting at this size even with AA tuned.
+ var badge_texture := TextureRect.new()
+ badge_texture.mouse_filter = Control.MOUSE_FILTER_IGNORE
+ badge_texture.texture = BADGE_CIRCLE_OUTLINE_ICON
+ badge_texture.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
+ badge_texture.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
+ badge_wrap.add_child(badge_texture)
+ choice_badge_textures.append(badge_texture)
+
var badge := Label.new()
badge.mouse_filter = Control.MOUSE_FILTER_IGNORE
badge.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
badge.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
- badge.anchor_left = 0.0
- badge.anchor_top = 0.0
- badge.anchor_right = 0.0
- badge.anchor_bottom = 0.0
- badge.offset_left = 12
- badge.offset_top = 12
- badge.offset_right = 42
- badge.offset_bottom = 42
- _set_scaled_label(badge, 14, Brand.muted_color(true))
- button.add_child(badge)
+ badge.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
+ _set_scaled_label(badge, 15, Brand.muted_color(true))
+ badge_wrap.add_child(badge)
choice_badges.append(badge)
var percent_label := Label.new()
@@ -469,9 +488,9 @@ func _ready() -> void:
percent_label.anchor_right = 1.0
percent_label.anchor_bottom = 0.0
percent_label.offset_left = -82
- percent_label.offset_top = 8
+ percent_label.offset_top = 18
percent_label.offset_right = -18
- percent_label.offset_bottom = 40
+ percent_label.offset_bottom = 50
_set_scaled_label(percent_label, 18, Style.TEXT)
button.add_child(percent_label)
choice_percent_labels.append(percent_label)
@@ -486,10 +505,10 @@ func _ready() -> void:
top_label.anchor_top = 0.0
top_label.anchor_right = 1.0
top_label.anchor_bottom = 0.0
- top_label.offset_left = 54
- top_label.offset_top = 10
+ top_label.offset_left = 62
+ top_label.offset_top = 18
top_label.offset_right = -96
- top_label.offset_bottom = 42
+ top_label.offset_bottom = 50
_set_scaled_rich_text(top_label, 16)
top_label.add_theme_color_override("default_color", Style.RED)
button.add_child(top_label)
@@ -682,10 +701,10 @@ func _question_content_box(dark: bool) -> StyleBoxFlat:
func _answer_box(dark: bool, state: String) -> StyleBoxFlat:
var box := StyleBoxFlat.new()
- box.content_margin_left = 54
+ box.content_margin_left = 62
box.content_margin_right = 82
- box.content_margin_top = 16
- box.content_margin_bottom = 16
+ box.content_margin_top = 22
+ box.content_margin_bottom = 22
if state == "default" or state == "eliminated":
# Flat, divider-style row - most choices sit in a plain list; only the
# states below need to visually pop out as their own card.
@@ -3015,8 +3034,12 @@ func _style_choice_button_for_state(index: int, choice_id: String) -> void:
if state == "selected": accent = Brand.PURPLE
elif state == "correct": accent = Brand.SUCCESS
elif state == "wrong": accent = Color("fb7185")
- badge.add_theme_stylebox_override("normal", Brand.icon_circle_box(dark, accent, 15, state in ["selected", "correct", "wrong"]))
- badge.add_theme_color_override("font_color", Color.WHITE if state in ["selected", "correct", "wrong"] else Brand.muted_color(dark))
+ var filled := state in ["selected", "correct", "wrong"]
+ if index < choice_badge_textures.size():
+ var badge_texture := choice_badge_textures[index]
+ badge_texture.texture = BADGE_CIRCLE_FILLED_ICON if filled else BADGE_CIRCLE_OUTLINE_ICON
+ badge_texture.modulate = accent
+ badge.add_theme_color_override("font_color", Color.WHITE if filled else Brand.muted_color(dark))
func _mark_review_input_handled() -> void: