Add persistent light and dark themes

This commit is contained in:
hz
2026-07-14 13:39:42 -05:00
parent 04694e4932
commit abbe6cf9f8
8 changed files with 315 additions and 26 deletions
+53 -9
View File
@@ -18,6 +18,7 @@ const REPORT_CATEGORIES := [
]
var main: Control
var theme_service
var choice_buttons: Array[Button] = []
var choice_percent_labels: Array[Label] = []
var choice_top_labels: Array[Label] = []
@@ -55,6 +56,7 @@ var text_larger_button: Button
var labs_button: Button
var flag_button: Button
var report_button: Button
var theme_button: Button
var question_toolbar: HBoxContainer
var nav_palette: HBoxContainer
var question_content: VBoxContainer
@@ -86,6 +88,7 @@ var shake_time := 0.0
var shake_origin := Vector2.ZERO
func _ready() -> void:
theme_service = get_node("/root/UIThemeService")
set_anchors_preset(Control.PRESET_FULL_RECT)
add_theme_constant_override("separation", 10)
if not QuestionBankService.choice_percentages_updated.is_connected(_on_choice_percentages_updated):
@@ -131,6 +134,14 @@ func _ready() -> void:
question_toolbar.add_theme_constant_override("separation", 8)
question_toolbar.alignment = BoxContainer.ALIGNMENT_END
theme_button = Button.new()
theme_button.custom_minimum_size = Vector2(44, 44)
theme_button.add_theme_font_size_override("font_size", 22)
UISoundService.set_button_sound(theme_button, "toggle")
theme_button.pressed.connect(theme_service.toggle_theme)
question_toolbar.add_child(theme_button)
_refresh_theme_button()
text_smaller_button = Button.new()
text_smaller_button.text = "A-"
text_smaller_button.tooltip_text = "Decrease text size"
@@ -622,12 +633,12 @@ func _submit_or_next() -> void:
if current_answer.is_empty():
if selected_choice == "":
return
var question_id_for_attempt := str(BlockSessionService.get_current_question().get("id", ""))
if BlockSessionService.session_kind == "core":
_collapse_resolved_followups()
var attempted_choice := selected_choice
var attempted_question_id := str(BlockSessionService.get_current_question().get("id", ""))
QuestionBankService.record_first_attempt(
attempted_question_id,
question_id_for_attempt,
attempted_choice,
_presented_choice_ids()
)
@@ -649,9 +660,8 @@ func _submit_or_next() -> void:
_trigger_wrong_feedback()
_apply_saved_choice_state()
return
var attempted_question_id := str(BlockSessionService.get_current_question().get("id", ""))
QuestionBankService.record_first_attempt(
attempted_question_id,
question_id_for_attempt,
selected_choice,
_presented_choice_ids()
)
@@ -742,7 +752,7 @@ func _trigger_wrong_feedback() -> void:
green_fog_time = 0.0
red_fog_time = FEEDBACK_FADE_SECONDS
if feedback_fog != null:
feedback_fog.color = Color(1.0, 0.0, 0.0, 0.26)
feedback_fog.color = Color(1.0, 0.0, 0.0, _feedback_overlay_alpha(false))
func _trigger_correct_feedback() -> void:
@@ -757,7 +767,7 @@ func _trigger_correct_feedback() -> void:
for button in choice_buttons:
button.disabled = true
if feedback_fog != null:
feedback_fog.color = Color(0.25, 1.0, 0.25, 0.23)
feedback_fog.color = Color(0.25, 1.0, 0.25, _feedback_overlay_alpha(true))
func _update_answer_feedback(delta: float) -> void:
@@ -770,11 +780,11 @@ func _update_answer_feedback(delta: float) -> void:
if red_fog_time > 0.0 and feedback_fog != null:
red_fog_time = max(0.0, red_fog_time - delta)
var red_alpha := 0.26 * (red_fog_time / FEEDBACK_FADE_SECONDS)
var red_alpha := _feedback_overlay_alpha(false) * (red_fog_time / FEEDBACK_FADE_SECONDS)
feedback_fog.color = Color(1.0, 0.0, 0.0, red_alpha)
elif green_fog_time > 0.0 and feedback_fog != null:
green_fog_time = max(0.0, green_fog_time - delta)
var green_alpha := 0.23 * (green_fog_time / FEEDBACK_FADE_SECONDS)
var green_alpha := _feedback_overlay_alpha(true) * (green_fog_time / FEEDBACK_FADE_SECONDS)
feedback_fog.color = Color(0.25, 1.0, 0.25, green_alpha)
if pending_correct_advance:
@@ -787,6 +797,12 @@ func _update_answer_feedback(delta: float) -> void:
_advance_or_finish()
func _feedback_overlay_alpha(is_correct: bool) -> float:
if Style.is_dark:
return 0.23 if is_correct else 0.26
return 0.07
func _reset_wrong_feedback() -> void:
if shake_time > 0.0:
position = shake_origin
@@ -853,6 +869,7 @@ func _toggle_report_current_question() -> void:
func _refresh_toolbar_state() -> void:
_refresh_theme_button()
_refresh_labs_button()
if flag_button != null:
Style.style_chip(flag_button, Style.AMBER if flagged else Style.CYAN)
@@ -862,6 +879,30 @@ func _refresh_toolbar_state() -> void:
report_button.tooltip_text = "Report submitted; report another issue" if reported_question_ids.has(question_id) else "Report question"
func refresh_theme_controls() -> void:
_refresh_theme_button()
if labs_button != null:
labs_button.icon = _make_labs_icon()
if flag_button != null:
flag_button.icon = _make_flag_icon()
if report_button != null:
report_button.icon = _make_report_icon()
if question_toolbar != null:
for child in question_toolbar.get_children():
if child is Button and child.tooltip_text == "Clear highlights":
child.icon = _make_clear_highlights_icon()
_refresh_toolbar_state()
_apply_saved_choice_state()
func _refresh_theme_button() -> void:
if theme_button == null:
return
theme_button.text = "☀" if theme_service.is_dark_mode() else "☾"
theme_button.tooltip_text = "Switch to light mode" if theme_service.is_dark_mode() else "Switch to dark mode"
Style.style_chip(theme_button, Style.CYAN)
func _refresh_player_stats() -> void:
if player_stats_label == null:
return
@@ -895,6 +936,7 @@ func _set_scaled_button_font(button: Button, base_size: int) -> void:
func _set_scaled_rich_text(label: RichTextLabel, base_size: int) -> void:
label.set_meta("question_base_font_size", base_size)
label.add_theme_color_override("default_color", Style.TEXT)
label.add_theme_font_size_override("normal_font_size", _scaled_font_size(base_size))
label.add_theme_font_size_override("bold_font_size", _scaled_font_size(base_size))
@@ -1493,7 +1535,7 @@ func _apply_saved_choice_state() -> void:
choice_buttons[index].disabled = locked_wrong_choice_ids.has(id) and not has_followup_panel
_style_choice_button_for_state(index, id)
if locked_wrong_choice_ids.has(id):
choice_buttons[index].modulate = Color(1.0, 0.25, 0.25)
choice_buttons[index].modulate = Color(1.0, 0.25, 0.25) if Style.is_dark else Color.WHITE
elif eliminated.has(id):
choice_buttons[index].modulate = Color(0.45, 0.45, 0.45)
elif id == selected_choice:
@@ -1754,6 +1796,8 @@ func _style_followup_result_button(button: Button, is_correct: bool) -> void:
button.add_theme_stylebox_override("hover", Style.button_box(fill, border))
button.add_theme_stylebox_override("disabled", Style.button_box(fill, border))
button.add_theme_color_override("font_color", Style.TEXT)
button.add_theme_color_override("font_hover_color", Style.TEXT)
button.add_theme_color_override("font_disabled_color", Style.TEXT)
func _unlock_after_followup() -> void: