Reach Settings/Profile from the pause menu, with a way back
The pause menu (Escape during a question/CCS/flashcards run) only had Resume/UI Sounds/Exit Run/Log Out. Settings already has an equivalent "Interface sounds" toggle, so drop the duplicate from the pause menu. More importantly, "player" and "ccs_player" hide the entire app shell (header + sidebar), so there was no way to reach Settings or Profile at all mid-session - add Profile/Settings buttons to the pause menu to close that gap. Navigating there follows the existing return_screen payload convention (already used by simulation_history/results/review) rather than inventing new navigation infrastructure: _navigate_from_pause() remembers current_screen_name and passes it through show_screen's payload. Both screens show a "Back to session" button when that's set, and hide it otherwise (including on ordinary non-paused visits). show_screen() unconditionally resumes the question timer via _close_app_menu() as a side effect of any navigation, so _navigate_from_pause() re-pauses it immediately when leaving "player" - the timer then resumes for real, for free, when the user actually returns via show_screen(return_screen). progress_profile.gd rebuilds its whole child tree on every theme toggle (_rebuild -> _build), which would otherwise wipe the button's visibility - added _refresh_back_to_session_visibility(), called both after _build() and from on_show(), so it survives a theme change while paused. Flipped tests/web_responsive_test.gd's assertion, which previously required the pause menu NOT to have a Profile button. Verified end-to-end headlessly: opening the pause menu from a live "player" session, navigating to Settings and Profile, confirming current_question_pause_started_at is correctly paused while away and resumed on return, confirming the back-to-session button appears only when reached via pause (not on ordinary nav), and confirming it survives a dark/light theme toggle on the Profile screen.
This commit is contained in:
+23
-9
@@ -11,7 +11,6 @@ var header_title: Label
|
||||
var environment_badge: Label
|
||||
var app_menu: PanelContainer
|
||||
var menu_exit_button: Button
|
||||
var sound_toggle: CheckButton
|
||||
var theme_button: Button
|
||||
var feedback_button: Button
|
||||
var feedback_dialog: Control
|
||||
@@ -855,7 +854,7 @@ func _build_app_menu() -> void:
|
||||
app_menu.visible = false
|
||||
app_menu.top_level = true
|
||||
app_menu.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
app_menu.custom_minimum_size = Vector2(340, 230)
|
||||
app_menu.custom_minimum_size = Vector2(340, 290)
|
||||
app_menu.add_theme_stylebox_override("panel", Style.panel_box(Color("172337"), Style.CYAN))
|
||||
add_child(app_menu)
|
||||
|
||||
@@ -877,13 +876,21 @@ func _build_app_menu() -> void:
|
||||
resume.pressed.connect(_close_app_menu)
|
||||
content.add_child(resume)
|
||||
|
||||
sound_toggle = CheckButton.new()
|
||||
sound_toggle.text = "UI Sounds"
|
||||
sound_toggle.button_pressed = UISoundService.are_sounds_enabled()
|
||||
sound_toggle.custom_minimum_size = Vector2(0, 44)
|
||||
UISoundService.set_button_sound(sound_toggle, "none")
|
||||
sound_toggle.toggled.connect(UISoundService.set_sounds_enabled)
|
||||
content.add_child(sound_toggle)
|
||||
var profile_button := Button.new()
|
||||
profile_button.text = "Profile"
|
||||
profile_button.custom_minimum_size = Vector2(0, 48)
|
||||
Style.style_chip(profile_button, Style.CYAN)
|
||||
UISoundService.set_button_sound(profile_button, "open")
|
||||
profile_button.pressed.connect(_navigate_from_pause.bind("profile"))
|
||||
content.add_child(profile_button)
|
||||
|
||||
var settings_button := Button.new()
|
||||
settings_button.text = "Settings"
|
||||
settings_button.custom_minimum_size = Vector2(0, 48)
|
||||
Style.style_chip(settings_button, Style.CYAN)
|
||||
UISoundService.set_button_sound(settings_button, "open")
|
||||
settings_button.pressed.connect(_navigate_from_pause.bind("settings"))
|
||||
content.add_child(settings_button)
|
||||
|
||||
menu_exit_button = Button.new()
|
||||
menu_exit_button.text = "Exit Run"
|
||||
@@ -929,6 +936,13 @@ func _close_app_menu() -> void:
|
||||
BlockSessionService.resume_current_question_timer()
|
||||
|
||||
|
||||
func _navigate_from_pause(target_screen: String) -> void:
|
||||
var return_screen := current_screen_name
|
||||
show_screen(target_screen, {"return_screen": return_screen})
|
||||
if return_screen == "player":
|
||||
BlockSessionService.pause_current_question_timer()
|
||||
|
||||
|
||||
func _exit_run() -> void:
|
||||
_close_app_menu()
|
||||
if current_screen_name == "ccs_player":
|
||||
|
||||
@@ -19,6 +19,8 @@ var outcome_consent: CheckBox
|
||||
var outcome_status: Label
|
||||
var outcome_delete: Button
|
||||
var connected_accounts_list: VBoxContainer
|
||||
var return_screen := ""
|
||||
var back_to_session_button: Button
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
@@ -38,7 +40,10 @@ func _ready() -> void:
|
||||
_build()
|
||||
|
||||
|
||||
func on_show(_payload: Variant = null) -> void:
|
||||
func on_show(payload: Variant = null) -> void:
|
||||
var wrapper: Dictionary = payload if typeof(payload) == TYPE_DICTIONARY else {}
|
||||
return_screen = str(wrapper.get("return_screen", ""))
|
||||
_refresh_back_to_session_visibility()
|
||||
_render()
|
||||
AccountProfileService.refresh()
|
||||
DashboardService.refresh()
|
||||
@@ -63,6 +68,11 @@ func _build() -> void:
|
||||
status_label = _label("", 13, Brand.muted_color(_dark()))
|
||||
status_label.visible = false
|
||||
page.add_child(status_label)
|
||||
back_to_session_button = _button("Back to session", "", true)
|
||||
back_to_session_button.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
|
||||
UISoundService.set_button_sound(back_to_session_button, "back")
|
||||
back_to_session_button.pressed.connect(_return_to_session)
|
||||
page.add_child(back_to_session_button)
|
||||
columns = GridContainer.new()
|
||||
columns.columns = 2
|
||||
columns.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
@@ -87,6 +97,7 @@ func _build() -> void:
|
||||
right.add_child(_outcome_card())
|
||||
right.add_child(_connected_accounts_card())
|
||||
right.add_child(_account_actions_card())
|
||||
_refresh_back_to_session_visibility()
|
||||
_update_responsive()
|
||||
_render()
|
||||
|
||||
@@ -510,6 +521,17 @@ func _button(text: String, icon_name: String = "", primary: bool = false) -> But
|
||||
return button
|
||||
|
||||
|
||||
func _refresh_back_to_session_visibility() -> void:
|
||||
if back_to_session_button != null:
|
||||
back_to_session_button.visible = not return_screen.is_empty()
|
||||
|
||||
|
||||
func _return_to_session() -> void:
|
||||
if return_screen.is_empty():
|
||||
return
|
||||
main.show_screen(return_screen)
|
||||
|
||||
|
||||
func _action_button(text: String, icon_name: String, color: Color = Color.TRANSPARENT) -> Button:
|
||||
var button := _button(text, icon_name, false)
|
||||
button.alignment = HORIZONTAL_ALIGNMENT_LEFT
|
||||
|
||||
@@ -13,6 +13,8 @@ var followups_toggle: CheckButton
|
||||
var question_scale_value_label: Label
|
||||
var ccs_scale_value_label: Label
|
||||
var core_block_size_value_label: Label
|
||||
var return_screen := ""
|
||||
var back_to_session_button: Button
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
@@ -39,6 +41,17 @@ func _ready() -> void:
|
||||
subtitle.add_theme_color_override("font_color", Brand.SLATE)
|
||||
add_child(subtitle)
|
||||
|
||||
back_to_session_button = Button.new()
|
||||
back_to_session_button.text = "Back to session"
|
||||
back_to_session_button.custom_minimum_size = Vector2(0, 44)
|
||||
back_to_session_button.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
|
||||
back_to_session_button.add_theme_stylebox_override("normal", Brand.gradient_button_box())
|
||||
back_to_session_button.add_theme_color_override("font_color", Color.WHITE)
|
||||
back_to_session_button.visible = false
|
||||
UISoundService.set_button_sound(back_to_session_button, "back")
|
||||
back_to_session_button.pressed.connect(_return_to_session)
|
||||
add_child(back_to_session_button)
|
||||
|
||||
var panel := PanelContainer.new()
|
||||
panel.custom_minimum_size = Vector2(520, 0)
|
||||
panel.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
|
||||
@@ -98,7 +111,10 @@ func _ready() -> void:
|
||||
_refresh_text_scale_labels()
|
||||
|
||||
|
||||
func on_show(_payload: Variant = null) -> void:
|
||||
func on_show(payload: Variant = null) -> void:
|
||||
var wrapper: Dictionary = payload if typeof(payload) == TYPE_DICTIONARY else {}
|
||||
return_screen = str(wrapper.get("return_screen", ""))
|
||||
back_to_session_button.visible = not return_screen.is_empty()
|
||||
theme_toggle.set_pressed_no_signal(UIThemeService.is_dark_mode())
|
||||
sound_toggle.set_pressed_no_signal(UISoundService.are_sounds_enabled())
|
||||
followups_toggle.set_pressed_no_signal(BlockSessionService.show_review_followups)
|
||||
@@ -209,6 +225,12 @@ func _open_legal_document(kind: String) -> void:
|
||||
main.open_legal_document(kind)
|
||||
|
||||
|
||||
func _return_to_session() -> void:
|
||||
if return_screen.is_empty():
|
||||
return
|
||||
main.show_screen(return_screen)
|
||||
|
||||
|
||||
func _on_theme_changed(is_dark: bool) -> void:
|
||||
theme_toggle.set_pressed_no_signal(is_dark)
|
||||
var panel := theme_toggle.get_parent().get_parent() as PanelContainer
|
||||
|
||||
@@ -50,10 +50,13 @@ func _run() -> void:
|
||||
_assert(main_screen.account_chevron_button.get_index() == main_screen.account_button.get_index() + 1, "the account avatar and chevron should remain adjacent")
|
||||
_assert(main_screen.header_panel.find_child("HeaderStats", true, false) == null, "the application header should not display rating or role labels")
|
||||
var pause_menu_has_profile := false
|
||||
var pause_menu_has_settings := false
|
||||
for menu_button in main_screen.app_menu.find_children("*", "Button", true, false):
|
||||
if menu_button.text == "Profile":
|
||||
pause_menu_has_profile = true
|
||||
_assert(not pause_menu_has_profile, "the paused-run menu should not duplicate profile navigation")
|
||||
if menu_button.text == "Settings":
|
||||
pause_menu_has_settings = true
|
||||
_assert(pause_menu_has_profile and pause_menu_has_settings, "the paused-run menu should offer Profile and Settings navigation")
|
||||
_assert(main_screen.session_expiry_dialog != null, "the application shell should construct a global session-expiry prompt")
|
||||
main_screen.session_expiry_dialog._show_warning(120)
|
||||
_assert(main_screen.session_expiry_dialog.visible, "the session-expiry prompt should cover every authenticated screen")
|
||||
|
||||
Reference in New Issue
Block a user