Redesign study navigation and light theme
This commit is contained in:
+468
-46
@@ -1,6 +1,8 @@
|
||||
extends Control
|
||||
|
||||
const Style := preload("res://scripts/ui/ui_style.gd")
|
||||
const Brand := preload("res://scripts/ui/medmind_style.gd")
|
||||
const NavigationRoutes := preload("res://scripts/ui/navigation_routes.gd")
|
||||
|
||||
var screens := {}
|
||||
var header_stats: Label
|
||||
@@ -15,6 +17,20 @@ var feedback_dialog: Control
|
||||
var theme_service
|
||||
var current_screen_name := ""
|
||||
var screens_margin: MarginContainer
|
||||
var screens_host: MarginContainer
|
||||
var shell_root: HBoxContainer
|
||||
var sidebar: PanelContainer
|
||||
var sidebar_content: VBoxContainer
|
||||
var drawer_backdrop: ColorRect
|
||||
var drawer_open := false
|
||||
var menu_button: Button
|
||||
var account_button: Button
|
||||
var account_chevron_button: Button
|
||||
var account_menu: PanelContainer
|
||||
var nav_buttons: Dictionary = {}
|
||||
var current_nav_key := "home"
|
||||
var compact_shell := false
|
||||
var app_background: ColorRect
|
||||
|
||||
func _ready() -> void:
|
||||
theme_service = get_node("/root/UIThemeService")
|
||||
@@ -27,8 +43,16 @@ func _ready() -> void:
|
||||
|
||||
|
||||
func _update_responsive_layout() -> void:
|
||||
compact_shell = size.x < 900.0
|
||||
if sidebar != null:
|
||||
sidebar.visible = AuthState.is_logged_in and not compact_shell and _screen_uses_shell(current_screen_name)
|
||||
if menu_button != null:
|
||||
menu_button.visible = AuthState.is_logged_in and compact_shell and _screen_uses_shell(current_screen_name)
|
||||
_apply_screen_margins(current_screen_name)
|
||||
if header_stats != null:
|
||||
header_stats.visible = size.x >= 900.0
|
||||
header_stats.visible = size.x >= 720.0
|
||||
if drawer_open and not compact_shell:
|
||||
_close_drawer()
|
||||
if app_menu != null:
|
||||
app_menu.custom_minimum_size.x = minf(340.0, maxf(280.0, size.x - 32.0))
|
||||
if app_menu.visible:
|
||||
@@ -38,7 +62,9 @@ func _update_responsive_layout() -> void:
|
||||
|
||||
func show_screen(screen_name: String, payload: Variant = null) -> void:
|
||||
_close_app_menu()
|
||||
for child in $Root/Screens.get_children():
|
||||
_close_account_menu()
|
||||
_close_drawer()
|
||||
for child in screens_host.get_children():
|
||||
if child.visible and child.has_method("on_hide"):
|
||||
child.on_hide()
|
||||
child.visible = false
|
||||
@@ -48,88 +74,133 @@ func show_screen(screen_name: String, payload: Variant = null) -> void:
|
||||
return
|
||||
|
||||
current_screen_name = screen_name
|
||||
if screens_margin != null:
|
||||
var margin := 0 if screen_name == "login" else 24
|
||||
screens_margin.add_theme_constant_override("margin_left", margin)
|
||||
screens_margin.add_theme_constant_override("margin_right", margin)
|
||||
screens_margin.add_theme_constant_override("margin_top", 0 if screen_name == "login" else 18)
|
||||
screens_margin.add_theme_constant_override("margin_bottom", 0 if screen_name == "login" else 22)
|
||||
_apply_screen_margins(screen_name)
|
||||
screen.visible = true
|
||||
if header_panel != null:
|
||||
header_panel.visible = screen_name not in ["login", "player", "ccs_player"]
|
||||
header_panel.visible = _screen_uses_shell(screen_name)
|
||||
_update_shell_visibility(screen_name)
|
||||
_update_active_navigation(screen_name, payload)
|
||||
if screen.has_method("on_show"):
|
||||
screen.on_show(payload)
|
||||
|
||||
|
||||
func _build_ui() -> void:
|
||||
var background := ColorRect.new()
|
||||
background.color = Style.BACKGROUND
|
||||
background.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
add_child(background)
|
||||
func _apply_screen_margins(screen_name: String) -> void:
|
||||
if screens_margin == null or screen_name.is_empty():
|
||||
return
|
||||
var margin := 0 if screen_name == "login" else (20 if compact_shell else 44)
|
||||
screens_margin.add_theme_constant_override("margin_left", margin)
|
||||
screens_margin.add_theme_constant_override("margin_right", margin)
|
||||
screens_margin.add_theme_constant_override("margin_top", 0 if screen_name == "login" else (20 if compact_shell else 34))
|
||||
screens_margin.add_theme_constant_override("margin_bottom", 0 if screen_name == "login" else 28)
|
||||
|
||||
var root := VBoxContainer.new()
|
||||
root.name = "Root"
|
||||
root.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
root.add_theme_constant_override("separation", 0)
|
||||
add_child(root)
|
||||
|
||||
func _build_ui() -> void:
|
||||
app_background = ColorRect.new()
|
||||
app_background.color = Brand.background_color(UIThemeService.is_dark_mode())
|
||||
app_background.material = Brand.background_material(UIThemeService.is_dark_mode())
|
||||
app_background.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
add_child(app_background)
|
||||
|
||||
shell_root = HBoxContainer.new()
|
||||
shell_root.name = "Root"
|
||||
shell_root.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
shell_root.add_theme_constant_override("separation", 0)
|
||||
add_child(shell_root)
|
||||
|
||||
sidebar = _build_sidebar()
|
||||
shell_root.add_child(sidebar)
|
||||
|
||||
var main_column := VBoxContainer.new()
|
||||
main_column.name = "MainColumn"
|
||||
main_column.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
main_column.add_theme_constant_override("separation", 0)
|
||||
shell_root.add_child(main_column)
|
||||
|
||||
header_panel = PanelContainer.new()
|
||||
header_panel.name = "Header"
|
||||
header_panel.add_theme_stylebox_override("panel", Style.panel_box(Style.SURFACE_DARK, Color("263752")))
|
||||
root.add_child(header_panel)
|
||||
header_panel.add_theme_stylebox_override("panel", _top_bar_box())
|
||||
main_column.add_child(header_panel)
|
||||
|
||||
var header := HBoxContainer.new()
|
||||
header.custom_minimum_size = Vector2(0, 78)
|
||||
header.custom_minimum_size = Vector2(0, 80)
|
||||
header.add_theme_constant_override("separation", 14)
|
||||
header.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
header_panel.add_child(header)
|
||||
|
||||
var title := Label.new()
|
||||
title.text = "MedMind"
|
||||
Style.set_label(title, 31, Style.TEXT)
|
||||
title.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
header.add_child(title)
|
||||
menu_button = Button.new()
|
||||
menu_button.icon = preload("res://assets/icons/lucide/menu.svg")
|
||||
menu_button.add_theme_constant_override("icon_max_width", 24)
|
||||
menu_button.tooltip_text = "Open navigation"
|
||||
menu_button.custom_minimum_size = Vector2(48, 48)
|
||||
menu_button.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||||
menu_button.visible = false
|
||||
menu_button.pressed.connect(_open_drawer)
|
||||
header.add_child(menu_button)
|
||||
|
||||
var header_spacer := Control.new()
|
||||
header_spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
header.add_child(header_spacer)
|
||||
|
||||
header_stats = Label.new()
|
||||
header_stats.name = "HeaderStats"
|
||||
Style.set_label(header_stats, 15, Style.MUTED)
|
||||
header_stats.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
header_stats.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
||||
header_stats.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||||
header.add_child(header_stats)
|
||||
|
||||
user_label = Label.new()
|
||||
Style.set_label(user_label, 15, Style.CYAN)
|
||||
user_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
||||
user_label.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
user_label.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND
|
||||
user_label.tooltip_text = "Open profile"
|
||||
user_label.gui_input.connect(_on_user_label_gui_input)
|
||||
user_label.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||||
header.add_child(user_label)
|
||||
|
||||
feedback_button = Button.new()
|
||||
feedback_button.text = "Feedback"
|
||||
feedback_button.tooltip_text = "Send general feedback or report a bug"
|
||||
feedback_button.custom_minimum_size = Vector2(100, 44)
|
||||
Style.style_chip(feedback_button, Style.CYAN)
|
||||
feedback_button.custom_minimum_size = Vector2(124, 42)
|
||||
feedback_button.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||||
_style_header_feedback_button(feedback_button)
|
||||
feedback_button.pressed.connect(open_feedback_dialog)
|
||||
header.add_child(feedback_button)
|
||||
|
||||
account_button = Button.new()
|
||||
account_button.tooltip_text = "Open account menu"
|
||||
account_button.custom_minimum_size = Vector2(44, 44)
|
||||
account_button.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||||
account_button.pressed.connect(_toggle_account_menu)
|
||||
_style_account_avatar(account_button)
|
||||
header.add_child(account_button)
|
||||
|
||||
account_chevron_button = Button.new()
|
||||
account_chevron_button.icon = preload("res://assets/icons/lucide/chevron-down.svg")
|
||||
account_chevron_button.tooltip_text = "Open account menu"
|
||||
account_chevron_button.custom_minimum_size = Vector2(28, 44)
|
||||
account_chevron_button.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||||
account_chevron_button.add_theme_constant_override("icon_max_width", 17)
|
||||
account_chevron_button.pressed.connect(_toggle_account_menu)
|
||||
_style_account_chevron(account_chevron_button)
|
||||
header.add_child(account_chevron_button)
|
||||
|
||||
theme_button = Button.new()
|
||||
theme_button.tooltip_text = "Switch color theme"
|
||||
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.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||||
theme_button.pressed.connect(theme_service.toggle_theme)
|
||||
_style_shell_theme_button(theme_button)
|
||||
header.add_child(theme_button)
|
||||
_refresh_theme_button()
|
||||
|
||||
var screens_container := MarginContainer.new()
|
||||
screens_margin = screens_container
|
||||
screens_host = screens_container
|
||||
screens_container.name = "Screens"
|
||||
screens_container.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
screens_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
screens_container.add_theme_constant_override("margin_left", 24)
|
||||
screens_container.add_theme_constant_override("margin_right", 24)
|
||||
screens_container.add_theme_constant_override("margin_top", 18)
|
||||
screens_container.add_theme_constant_override("margin_bottom", 22)
|
||||
root.add_child(screens_container)
|
||||
main_column.add_child(screens_container)
|
||||
|
||||
var menu := preload("res://scripts/ui/main_menu.gd").new()
|
||||
menu.name = "MainMenu"
|
||||
@@ -146,6 +217,7 @@ func _build_ui() -> void:
|
||||
var selector := preload("res://scripts/ui/study_selector.gd").new()
|
||||
selector.name = "StudySelector"
|
||||
selector.main = self
|
||||
selector.stage_changed.connect(_on_selector_stage_changed)
|
||||
screens_container.add_child(selector)
|
||||
screens["selection"] = selector
|
||||
|
||||
@@ -212,9 +284,17 @@ func _build_ui() -> void:
|
||||
screens_container.add_child(profile)
|
||||
screens["profile"] = profile
|
||||
|
||||
var settings := preload("res://scripts/ui/settings_screen.gd").new()
|
||||
settings.name = "SettingsScreen"
|
||||
settings.main = self
|
||||
screens_container.add_child(settings)
|
||||
screens["settings"] = settings
|
||||
|
||||
feedback_dialog = preload("res://scripts/ui/app_feedback_dialog.gd").new()
|
||||
feedback_dialog.name = "AppFeedbackDialog"
|
||||
add_child(feedback_dialog)
|
||||
_build_account_menu()
|
||||
_build_drawer()
|
||||
|
||||
ProfileService.profile_changed.connect(_refresh_header_stats)
|
||||
QuestionBankService.ratings_changed.connect(_refresh_header_stats)
|
||||
@@ -223,26 +303,345 @@ func _build_ui() -> void:
|
||||
_build_app_menu()
|
||||
|
||||
|
||||
func _build_sidebar() -> PanelContainer:
|
||||
var panel := PanelContainer.new()
|
||||
panel.name = "Sidebar"
|
||||
panel.custom_minimum_size = Vector2(304, 0)
|
||||
panel.add_theme_stylebox_override("panel", _sidebar_box())
|
||||
sidebar_content = VBoxContainer.new()
|
||||
sidebar_content.add_theme_constant_override("separation", 8)
|
||||
panel.add_child(sidebar_content)
|
||||
_populate_navigation(sidebar_content, true)
|
||||
return panel
|
||||
|
||||
|
||||
func _populate_navigation(container: VBoxContainer, include_brand: bool) -> void:
|
||||
if include_brand:
|
||||
var brand_row := HBoxContainer.new()
|
||||
brand_row.custom_minimum_size = Vector2(0, 56)
|
||||
brand_row.add_theme_constant_override("separation", 10)
|
||||
container.add_child(brand_row)
|
||||
var mark := TextureRect.new()
|
||||
mark.texture = preload("res://assets/branding/medmind-icon.png")
|
||||
mark.material = Brand.logo_cutout_material()
|
||||
mark.custom_minimum_size = Vector2(46, 46)
|
||||
mark.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||
mark.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||
brand_row.add_child(mark)
|
||||
var wordmark := HBoxContainer.new()
|
||||
wordmark.add_theme_constant_override("separation", 0)
|
||||
brand_row.add_child(wordmark)
|
||||
var med := Label.new()
|
||||
med.text = "Med"
|
||||
med.add_theme_font_override("font", Brand.font)
|
||||
med.add_theme_font_size_override("font_size", 27)
|
||||
wordmark.add_child(med)
|
||||
var mind := Label.new()
|
||||
mind.text = "Mind"
|
||||
mind.add_theme_font_override("font", Brand.font)
|
||||
mind.add_theme_font_size_override("font_size", 27)
|
||||
mind.add_theme_color_override("font_color", Brand.PURPLE)
|
||||
wordmark.add_child(mind)
|
||||
container.add_child(HSeparator.new())
|
||||
|
||||
_add_nav_button(container, "home", "Home", "house", true)
|
||||
_add_nav_button(container, "practice", "Practice", "pencil", true)
|
||||
_add_nav_button(container, "exams", "Exams", "clipboard-list", true)
|
||||
_add_nav_button(container, "ccs", "CCS Cases", "stethoscope", true)
|
||||
_add_nav_button(container, "performance", "Performance", "chart-no-axes-column-increasing", true)
|
||||
_add_nav_button(container, "flashcards", "Flashcards", "copy", false)
|
||||
_add_nav_button(container, "study_plan", "Study Plan", "calendar-days", true)
|
||||
_add_nav_button(container, "bookmarks", "Bookmarks", "bookmark", false)
|
||||
_add_nav_button(container, "notes", "Notes", "notebook-pen", false)
|
||||
|
||||
var spacer := Control.new()
|
||||
spacer.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
container.add_child(spacer)
|
||||
container.add_child(HSeparator.new())
|
||||
_add_nav_button(container, "settings", "Settings", "settings", true)
|
||||
_add_nav_button(container, "logout", "Log out", "log-out", true)
|
||||
|
||||
|
||||
func _add_nav_button(container: VBoxContainer, key: String, label_text: String, icon_name: String, enabled: bool) -> void:
|
||||
var button := Button.new()
|
||||
button.text = label_text
|
||||
button.name = "Nav%s" % key.to_pascal_case()
|
||||
button.icon = load("res://assets/icons/lucide/%s.svg" % icon_name)
|
||||
button.expand_icon = true
|
||||
button.alignment = HORIZONTAL_ALIGNMENT_LEFT
|
||||
button.custom_minimum_size = Vector2(0, 58)
|
||||
button.add_theme_constant_override("icon_max_width", 25)
|
||||
button.add_theme_constant_override("h_separation", 18)
|
||||
button.add_theme_font_override("font", Brand.font)
|
||||
button.add_theme_font_size_override("font_size", 18)
|
||||
button.disabled = not enabled
|
||||
button.tooltip_text = "Coming Soon" if not enabled else label_text
|
||||
button.pressed.connect(_on_navigation_pressed.bind(key))
|
||||
container.add_child(button)
|
||||
if not nav_buttons.has(key):
|
||||
nav_buttons[key] = []
|
||||
var instances: Array = nav_buttons[key]
|
||||
instances.append(button)
|
||||
nav_buttons[key] = instances
|
||||
_style_nav_button(button, key == current_nav_key)
|
||||
|
||||
|
||||
func _on_navigation_pressed(key: String) -> void:
|
||||
match key:
|
||||
"home": show_screen("selection", "exam")
|
||||
"practice": show_screen("selection", "step3")
|
||||
"exams": show_screen("simulation_history")
|
||||
"ccs": show_screen("ccs_hub")
|
||||
"performance": show_screen("profile")
|
||||
"study_plan": show_screen("map")
|
||||
"settings": show_screen("settings")
|
||||
"logout": _logout()
|
||||
|
||||
|
||||
func _build_account_menu() -> void:
|
||||
account_menu = PanelContainer.new()
|
||||
account_menu.visible = false
|
||||
account_menu.top_level = true
|
||||
account_menu.custom_minimum_size = Vector2(230, 230)
|
||||
account_menu.add_theme_stylebox_override("panel", Brand.panel_box(UIThemeService.is_dark_mode()))
|
||||
add_child(account_menu)
|
||||
var content := VBoxContainer.new()
|
||||
content.add_theme_constant_override("separation", 8)
|
||||
account_menu.add_child(content)
|
||||
for spec in [["Profile", "user-round", "performance"], ["Settings", "settings", "settings"], ["Log out", "log-out", "logout"]]:
|
||||
var button := Button.new()
|
||||
button.text = spec[0]
|
||||
button.icon = load("res://assets/icons/lucide/%s.svg" % spec[1])
|
||||
button.add_theme_constant_override("icon_max_width", 22)
|
||||
button.alignment = HORIZONTAL_ALIGNMENT_LEFT
|
||||
button.custom_minimum_size = Vector2(0, 46)
|
||||
button.pressed.connect(_on_navigation_pressed.bind(spec[2]))
|
||||
content.add_child(button)
|
||||
|
||||
|
||||
func _build_drawer() -> void:
|
||||
drawer_backdrop = ColorRect.new()
|
||||
drawer_backdrop.visible = false
|
||||
drawer_backdrop.top_level = true
|
||||
drawer_backdrop.color = Color(0, 0, 0, 0.62)
|
||||
drawer_backdrop.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
drawer_backdrop.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
drawer_backdrop.gui_input.connect(_on_drawer_backdrop_input)
|
||||
add_child(drawer_backdrop)
|
||||
var drawer := PanelContainer.new()
|
||||
drawer.custom_minimum_size = Vector2(304, 0)
|
||||
drawer.set_anchors_preset(Control.PRESET_LEFT_WIDE)
|
||||
drawer.offset_right = 304
|
||||
drawer.add_theme_stylebox_override("panel", _sidebar_box())
|
||||
drawer.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
drawer_backdrop.add_child(drawer)
|
||||
var content := VBoxContainer.new()
|
||||
content.add_theme_constant_override("separation", 8)
|
||||
drawer.add_child(content)
|
||||
_populate_navigation(content, true)
|
||||
|
||||
|
||||
func _open_drawer() -> void:
|
||||
if drawer_backdrop == null:
|
||||
return
|
||||
drawer_open = true
|
||||
drawer_backdrop.visible = true
|
||||
|
||||
|
||||
func _close_drawer() -> void:
|
||||
drawer_open = false
|
||||
if drawer_backdrop != null:
|
||||
drawer_backdrop.visible = false
|
||||
|
||||
|
||||
func _on_drawer_backdrop_input(event: InputEvent) -> void:
|
||||
var click := event as InputEventMouseButton
|
||||
if click != null and click.pressed:
|
||||
_close_drawer()
|
||||
|
||||
|
||||
func _toggle_account_menu() -> void:
|
||||
if account_menu == null:
|
||||
return
|
||||
if account_menu.visible:
|
||||
_close_account_menu()
|
||||
return
|
||||
account_menu.visible = true
|
||||
account_menu.size = account_menu.custom_minimum_size
|
||||
account_menu.position = Vector2(
|
||||
get_viewport_rect().size.x - account_menu.size.x - 24,
|
||||
82
|
||||
)
|
||||
|
||||
|
||||
func _close_account_menu() -> void:
|
||||
if account_menu != null:
|
||||
account_menu.visible = false
|
||||
|
||||
|
||||
func _screen_uses_shell(screen_name: String) -> bool:
|
||||
return AuthState.is_logged_in and screen_name not in ["login", "player", "ccs_player"]
|
||||
|
||||
|
||||
func _update_shell_visibility(screen_name: String) -> void:
|
||||
var show_shell := _screen_uses_shell(screen_name)
|
||||
if sidebar != null:
|
||||
sidebar.visible = show_shell and not compact_shell
|
||||
if header_panel != null:
|
||||
header_panel.visible = show_shell
|
||||
if menu_button != null:
|
||||
menu_button.visible = show_shell and compact_shell
|
||||
|
||||
|
||||
func _update_active_navigation(screen_name: String, payload: Variant = null) -> void:
|
||||
var key := NavigationRoutes.key_for_screen(screen_name, payload)
|
||||
current_nav_key = key
|
||||
for nav_key in nav_buttons:
|
||||
for button in nav_buttons[nav_key]:
|
||||
_style_nav_button(button, nav_key == current_nav_key)
|
||||
|
||||
|
||||
func _on_selector_stage_changed(stage_name: String) -> void:
|
||||
if current_screen_name == "selection":
|
||||
_update_active_navigation("selection", stage_name)
|
||||
|
||||
|
||||
func _style_nav_button(button: Button, active: bool) -> void:
|
||||
var dark := UIThemeService.is_dark_mode()
|
||||
var normal := StyleBoxFlat.new()
|
||||
normal.bg_color = (Color("14255b") if dark else Color("e5edff")) if active else Color(0, 0, 0, 0)
|
||||
normal.set_corner_radius_all(10)
|
||||
normal.content_margin_left = 18
|
||||
normal.content_margin_right = 18
|
||||
button.add_theme_stylebox_override("normal", normal)
|
||||
var hover := normal.duplicate() as StyleBoxFlat
|
||||
hover.bg_color = Color("172d68") if dark else Color("d8e5ff")
|
||||
button.add_theme_stylebox_override("hover", hover)
|
||||
var disabled := normal.duplicate() as StyleBoxFlat
|
||||
disabled.bg_color = Color(0, 0, 0, 0)
|
||||
button.add_theme_stylebox_override("disabled", disabled)
|
||||
button.add_theme_color_override("font_color", (Color("f8fafc") if dark else Color("0f172a")) if active else (Color("b8c2d6") if dark else Color("53627a")))
|
||||
button.add_theme_color_override("icon_normal_color", Brand.ROYAL_BLUE if active else (Color("b8c2d6") if dark else Color("53627a")))
|
||||
button.add_theme_color_override("icon_hover_color", Brand.SKY_BLUE)
|
||||
button.add_theme_color_override("font_disabled_color", Color("596982") if dark else Color("94a3b8"))
|
||||
button.add_theme_color_override("icon_disabled_color", Color("596982") if dark else Color("94a3b8"))
|
||||
|
||||
|
||||
func _sidebar_box() -> StyleBoxFlat:
|
||||
var dark := UIThemeService.is_dark_mode()
|
||||
var box := Brand.shell_box(dark, "right")
|
||||
box.content_margin_left = 28
|
||||
box.content_margin_right = 28
|
||||
box.content_margin_top = 12
|
||||
box.content_margin_bottom = 26
|
||||
return box
|
||||
|
||||
|
||||
func _top_bar_box() -> StyleBoxFlat:
|
||||
var dark := UIThemeService.is_dark_mode()
|
||||
var box := Brand.shell_box(dark, "bottom")
|
||||
box.content_margin_left = 28
|
||||
box.content_margin_right = 28
|
||||
return box
|
||||
|
||||
|
||||
func _style_header_feedback_button(button: Button) -> void:
|
||||
var dark := UIThemeService.is_dark_mode()
|
||||
var normal := Style.button_box(Color(0, 0, 0, 0), Brand.ROYAL_BLUE)
|
||||
var hover := Style.button_box(Color(Brand.ROYAL_BLUE, 0.16), Brand.SKY_BLUE)
|
||||
var pressed := Style.button_box(Color(Brand.ROYAL_BLUE, 0.24), Brand.SKY_BLUE)
|
||||
button.add_theme_stylebox_override("normal", normal)
|
||||
button.add_theme_stylebox_override("hover", hover)
|
||||
button.add_theme_stylebox_override("pressed", pressed)
|
||||
button.add_theme_color_override("font_color", Brand.ROYAL_BLUE if not dark else Brand.SKY_BLUE)
|
||||
button.add_theme_color_override("font_hover_color", Brand.SKY_BLUE)
|
||||
button.add_theme_color_override("icon_normal_color", Brand.ROYAL_BLUE if not dark else Brand.SKY_BLUE)
|
||||
button.add_theme_color_override("icon_hover_color", Brand.SKY_BLUE)
|
||||
|
||||
|
||||
func _style_account_avatar(button: Button) -> void:
|
||||
var dark := UIThemeService.is_dark_mode()
|
||||
var normal := StyleBoxFlat.new()
|
||||
normal.bg_color = Color("26324f") if dark else Color("dce5f5")
|
||||
normal.corner_radius_top_left = 22
|
||||
normal.corner_radius_top_right = 22
|
||||
normal.corner_radius_bottom_left = 22
|
||||
normal.corner_radius_bottom_right = 22
|
||||
var hover := normal.duplicate() as StyleBoxFlat
|
||||
hover.bg_color = Color("344465") if dark else Color("cad7ed")
|
||||
button.add_theme_stylebox_override("normal", normal)
|
||||
button.add_theme_stylebox_override("hover", hover)
|
||||
button.add_theme_stylebox_override("pressed", hover)
|
||||
button.add_theme_color_override("font_color", Color("f8fafc") if dark else Color("172033"))
|
||||
button.add_theme_color_override("font_hover_color", Color("ffffff") if dark else Color("0f172a"))
|
||||
|
||||
|
||||
func _style_account_chevron(button: Button) -> void:
|
||||
var empty := StyleBoxEmpty.new()
|
||||
button.add_theme_stylebox_override("normal", empty)
|
||||
button.add_theme_stylebox_override("hover", empty)
|
||||
button.add_theme_stylebox_override("pressed", empty)
|
||||
button.add_theme_color_override("icon_normal_color", Style.MUTED)
|
||||
button.add_theme_color_override("icon_hover_color", Style.TEXT)
|
||||
|
||||
|
||||
func _style_shell_theme_button(button: Button) -> void:
|
||||
var dark := UIThemeService.is_dark_mode()
|
||||
var normal := StyleBoxFlat.new()
|
||||
normal.bg_color = Color("111b30") if dark else Brand.LIGHT_SURFACE
|
||||
normal.border_color = Color("33415d") if dark else Brand.LIGHT_BORDER
|
||||
normal.set_border_width_all(1)
|
||||
normal.set_corner_radius_all(22)
|
||||
var hover := normal.duplicate() as StyleBoxFlat
|
||||
hover.border_color = Brand.AMBER
|
||||
button.add_theme_stylebox_override("normal", normal)
|
||||
button.add_theme_stylebox_override("hover", hover)
|
||||
button.add_theme_stylebox_override("pressed", hover)
|
||||
button.add_theme_color_override("font_color", Brand.AMBER)
|
||||
button.add_theme_color_override("font_hover_color", Brand.AMBER)
|
||||
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if not AuthState.is_logged_in or current_screen_name == "login":
|
||||
return
|
||||
if event.is_action_pressed("ui_cancel"):
|
||||
_toggle_app_menu()
|
||||
get_viewport().set_input_as_handled()
|
||||
if drawer_open:
|
||||
_close_drawer()
|
||||
get_viewport().set_input_as_handled()
|
||||
return
|
||||
if account_menu != null and account_menu.visible:
|
||||
_close_account_menu()
|
||||
get_viewport().set_input_as_handled()
|
||||
return
|
||||
if current_screen_name in ["player", "ccs_player"]:
|
||||
_toggle_app_menu()
|
||||
get_viewport().set_input_as_handled()
|
||||
|
||||
|
||||
func _refresh_header_stats() -> void:
|
||||
if header_stats == null:
|
||||
return
|
||||
var profile := ProfileService.get_profile()
|
||||
header_stats.text = "Rating: %d | Streak %d" % [
|
||||
QuestionBankService.overall_elo,
|
||||
profile.get("current_streak", 0)
|
||||
]
|
||||
header_stats.text = "Rating: %d" % QuestionBankService.overall_elo
|
||||
if user_label != null:
|
||||
user_label.text = AuthState.username if AuthState.is_logged_in else ""
|
||||
user_label.text = AuthState.role.to_upper() if AuthState.is_logged_in and not AuthState.role.is_empty() else ""
|
||||
if account_button != null:
|
||||
account_button.text = _account_initials(AuthState.username)
|
||||
if feedback_button != null:
|
||||
feedback_button.visible = AuthState.is_logged_in
|
||||
feedback_button.icon = preload("res://assets/icons/lucide/message-square.svg")
|
||||
feedback_button.add_theme_constant_override("icon_max_width", 21)
|
||||
|
||||
|
||||
func _account_initials(username: String) -> String:
|
||||
var cleaned := ""
|
||||
for character in username:
|
||||
if character.to_lower() != character.to_upper():
|
||||
cleaned += character
|
||||
elif character.is_valid_int():
|
||||
cleaned += character
|
||||
if cleaned.length() >= 2:
|
||||
break
|
||||
return cleaned.to_upper() if not cleaned.is_empty() else "ME"
|
||||
|
||||
|
||||
func open_feedback_dialog() -> void:
|
||||
@@ -254,6 +653,29 @@ func open_feedback_dialog() -> void:
|
||||
func _on_theme_changed(is_dark_mode: bool) -> void:
|
||||
Style.refresh_tree(self, not is_dark_mode)
|
||||
_refresh_theme_button()
|
||||
if app_background != null:
|
||||
app_background.color = Brand.background_color(is_dark_mode)
|
||||
app_background.material = Brand.background_material(is_dark_mode)
|
||||
if account_menu != null:
|
||||
account_menu.add_theme_stylebox_override("panel", Brand.panel_box(is_dark_mode))
|
||||
if feedback_button != null:
|
||||
_style_header_feedback_button(feedback_button)
|
||||
if account_button != null:
|
||||
_style_account_avatar(account_button)
|
||||
if account_chevron_button != null:
|
||||
_style_account_chevron(account_chevron_button)
|
||||
if sidebar != null:
|
||||
sidebar.add_theme_stylebox_override("panel", _sidebar_box())
|
||||
if header_panel != null:
|
||||
header_panel.add_theme_stylebox_override("panel", _top_bar_box())
|
||||
if theme_button != null:
|
||||
_style_shell_theme_button(theme_button)
|
||||
for nav_key in nav_buttons:
|
||||
for button in nav_buttons[nav_key]:
|
||||
_style_nav_button(button, nav_key == current_nav_key)
|
||||
var selector = screens.get("selection")
|
||||
if selector != null and selector.has_method("refresh_theme_controls"):
|
||||
selector.refresh_theme_controls()
|
||||
var player = screens.get("player")
|
||||
if player != null and player.has_method("refresh_theme_controls"):
|
||||
player.refresh_theme_controls()
|
||||
@@ -264,7 +686,7 @@ func _refresh_theme_button() -> void:
|
||||
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)
|
||||
_style_shell_theme_button(theme_button)
|
||||
|
||||
|
||||
func _logout() -> void:
|
||||
|
||||
Reference in New Issue
Block a user