From 9f836c8765269cfd70b3bd64fa38879ed6cdad7d Mon Sep 17 00:00:00 2001 From: hz Date: Mon, 27 Jul 2026 00:43:18 -0500 Subject: [PATCH] Fix login/calculator theme bugs, gate flashcard button, hide system label pre-answer - Steam-linked login no longer writes literal "" into the email field (and no longer locks it read-only) when the verified identity has no email - Login screen releases focus on app focus-out so the username caret stops blinking while the window is inactive - Calculator display text stays legible in light mode; the LCD-style panel background is always dark, so its text should not follow the app theme - Question player: gate the "save as flashcard" toolbar button behind Brand.is_uat_environment(), matching the nav item, so it's hidden in prod - Hide the discipline/system label while a question is unanswered so it can't hint at the answer; reveal it once the explanation is shown - Top-align question content instead of vertically centering it, with a 20px top margin, so it sits closer to the progress indicator --- scripts/ui/calculator_panel.gd | 4 ++-- scripts/ui/login_screen.gd | 10 +++++++++- scripts/ui/question_player.gd | 16 +++++++--------- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/scripts/ui/calculator_panel.gd b/scripts/ui/calculator_panel.gd index ece38f6..04d15e0 100644 --- a/scripts/ui/calculator_panel.gd +++ b/scripts/ui/calculator_panel.gd @@ -101,8 +101,8 @@ func refresh_theme_controls() -> void: return var dark := UIThemeService.is_dark_mode() add_theme_stylebox_override("panel", Brand.content_card_box(dark, true, Brand.ROYAL_BLUE)) - display_label.add_theme_color_override("font_color", Brand.text_color(dark)) - operation_label.add_theme_color_override("font_color", Brand.muted_color(dark)) + display_label.add_theme_color_override("font_color", Color.WHITE) + operation_label.add_theme_color_override("font_color", Brand.muted_color(true)) for button in keypad_buttons: var accent := Brand.ROYAL_BLUE if str(button.get_meta("calculator_token", "")) in ["+", "−", "×", "÷", "="] else Brand.SLATE button.add_theme_stylebox_override("normal", Brand.badge_box(dark, accent)) diff --git a/scripts/ui/login_screen.gd b/scripts/ui/login_screen.gd index a1e9493..865453d 100644 --- a/scripts/ui/login_screen.gd +++ b/scripts/ui/login_screen.gd @@ -65,6 +65,13 @@ var compact_layout := false var layout_updating := false +func _notification(what: int) -> void: + if what == NOTIFICATION_APPLICATION_FOCUS_OUT: + var focus_owner := get_viewport().gui_get_focus_owner() + if focus_owner != null: + focus_owner.release_focus() + + func _ready() -> void: auth_base_url = EndpointConfig.resolve(AUTH_BASE, "/api/Auth") set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) @@ -851,7 +858,8 @@ func _on_steam_status(result: int, code: int, _headers: PackedStringArray, body: "requires_account": poll_timer.stop() _close_steam_popup() - external_verified_email = str(data.get("email", "")) + var verified_email_value: Variant = data.get("email") + external_verified_email = "" if verified_email_value == null else str(verified_email_value) steam_email_input.text = external_verified_email steam_email_input.editable = external_verified_email.is_empty() _set_mode("steam") diff --git a/scripts/ui/question_player.gd b/scripts/ui/question_player.gd index 86dad0d..ec200db 100644 --- a/scripts/ui/question_player.gd +++ b/scripts/ui/question_player.gd @@ -467,6 +467,7 @@ func _ready() -> void: content_margin.size_flags_vertical = Control.SIZE_EXPAND_FILL content_margin.add_theme_constant_override("margin_left", 4) content_margin.add_theme_constant_override("margin_right", 12) + content_margin.add_theme_constant_override("margin_top", 20) question_scroll.add_child(content_margin) question_content = VBoxContainer.new() @@ -1305,7 +1306,7 @@ func _load_question() -> void: if category_text.is_empty(): category_text = str(question.get("system", "")).strip_edges() category_label.text = category_text - category_label.visible = not category_text.is_empty() + category_label.visible = false _refresh_labs_button() presented_choices = question.get("presented_choices", question.get("choices", [])).duplicate(true) @@ -1716,6 +1717,7 @@ func _show_explanation() -> void: explanation_plain_text = str(question.get("explanation", "")) _render_explanation() explanation_label.visible = true + category_label.visible = not category_label.text.strip_edges().is_empty() _update_question_elo_label(true) _refresh_nav_palette() _update_content_vertical_alignment() @@ -1740,6 +1742,8 @@ func _clear_answer_review() -> void: explanation_label.visible = false if correct_review_panel != null: correct_review_panel.visible = false + if category_label != null: + category_label.visible = false _clear_container_children(teaching_cards) _clear_container_children(review_followups_content) review_followup_states.clear() @@ -1751,13 +1755,7 @@ func _clear_answer_review() -> void: func _update_content_vertical_alignment() -> void: if question_content == null: return - # Before an answer is submitted the stem+choices block is usually shorter than the - # available panel height; center it so it doesn't sit pinned to the top with a slab - # of empty space below. Once the answer review content appears, go back to top - # alignment so the growing explanation/follow-up sections read top-down and scroll - # naturally instead of being centered mid-panel. - var answered := correct_review_panel != null and correct_review_panel.visible - question_content.alignment = BoxContainer.ALIGNMENT_BEGIN if answered else BoxContainer.ALIGNMENT_CENTER + question_content.alignment = BoxContainer.ALIGNMENT_BEGIN func _clear_container_children(container: Control) -> void: @@ -2279,7 +2277,7 @@ func _refresh_toolbar_state() -> void: if notes_button != null: notes_button.visible = BlockSessionService.session_kind not in ["simulation", "dynamic"] if flashcard_button != null: - flashcard_button.visible = BlockSessionService.session_kind not in ["simulation", "dynamic"] + flashcard_button.visible = Brand.is_uat_environment() and BlockSessionService.session_kind not in ["simulation", "dynamic"] flashcard_button.disabled = current_answer.is_empty() if bookmark_button != null: bookmark_button.visible = BlockSessionService.session_kind not in ["simulation", "dynamic"]