Redesign study navigation and light theme
This commit is contained in:
+118
-44
@@ -8,6 +8,7 @@ var main: Control
|
||||
var auth_base_url := AUTH_BASE
|
||||
var background: ColorRect
|
||||
var left_brand: VBoxContainer
|
||||
var brand_tagline: RichTextLabel
|
||||
var auth_panel: PanelContainer
|
||||
var panel_content: VBoxContainer
|
||||
var heading: Label
|
||||
@@ -63,8 +64,9 @@ func _ready() -> void:
|
||||
poll_timer.timeout.connect(_poll_steam)
|
||||
add_child(poll_timer)
|
||||
get_viewport().size_changed.connect(_update_responsive_layout)
|
||||
resized.connect(_update_responsive_layout)
|
||||
UIThemeService.theme_changed.connect(_on_theme_changed)
|
||||
_update_responsive_layout()
|
||||
_update_responsive_layout.call_deferred()
|
||||
_set_mode("login")
|
||||
_apply_theme()
|
||||
|
||||
@@ -87,6 +89,7 @@ func _build_ui() -> void:
|
||||
add_child(background)
|
||||
|
||||
_add_dot_grid(Vector2(22, 24))
|
||||
_add_dot_grid(Vector2(22, 840))
|
||||
|
||||
theme_button = Button.new()
|
||||
theme_button.tooltip_text = "Switch color theme"
|
||||
@@ -98,14 +101,14 @@ func _build_ui() -> void:
|
||||
|
||||
var page_margin := MarginContainer.new()
|
||||
page_margin.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
page_margin.add_theme_constant_override("margin_left", 42)
|
||||
page_margin.add_theme_constant_override("margin_right", 42)
|
||||
page_margin.add_theme_constant_override("margin_top", 38)
|
||||
page_margin.add_theme_constant_override("margin_bottom", 38)
|
||||
page_margin.add_theme_constant_override("margin_left", 52)
|
||||
page_margin.add_theme_constant_override("margin_right", 52)
|
||||
page_margin.add_theme_constant_override("margin_top", 56)
|
||||
page_margin.add_theme_constant_override("margin_bottom", 56)
|
||||
add_child(page_margin)
|
||||
|
||||
var columns := HBoxContainer.new()
|
||||
columns.add_theme_constant_override("separation", 54)
|
||||
columns.add_theme_constant_override("separation", 72)
|
||||
page_margin.add_child(columns)
|
||||
|
||||
left_brand = VBoxContainer.new()
|
||||
@@ -119,35 +122,50 @@ func _build_ui() -> void:
|
||||
left_brand.add_child(brand_row)
|
||||
var large_icon := TextureRect.new()
|
||||
large_icon.texture = preload("res://assets/branding/medmind-icon.png")
|
||||
large_icon.custom_minimum_size = Vector2(172, 172)
|
||||
large_icon.custom_minimum_size = Vector2(190, 190)
|
||||
large_icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||
large_icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||
large_icon.material = Brand.logo_cutout_material()
|
||||
brand_row.add_child(large_icon)
|
||||
var wordmark := Label.new()
|
||||
wordmark.text = "MedMind"
|
||||
wordmark.add_theme_font_size_override("font_size", 76)
|
||||
wordmark.add_theme_font_override("font", Brand.font)
|
||||
var wordmark := HBoxContainer.new()
|
||||
wordmark.add_theme_constant_override("separation", 0)
|
||||
brand_row.add_child(wordmark)
|
||||
var tagline := Label.new()
|
||||
tagline.text = "Think like a physician."
|
||||
tagline.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
tagline.add_theme_font_size_override("font_size", 22)
|
||||
tagline.add_theme_font_override("font", Brand.font)
|
||||
left_brand.add_child(tagline)
|
||||
var med_wordmark := Label.new()
|
||||
med_wordmark.text = "Med"
|
||||
med_wordmark.add_theme_font_size_override("font_size", 76)
|
||||
med_wordmark.add_theme_font_override("font", Brand.font)
|
||||
wordmark.add_child(med_wordmark)
|
||||
var mind_wordmark := Label.new()
|
||||
mind_wordmark.text = "Mind"
|
||||
mind_wordmark.add_theme_font_size_override("font_size", 76)
|
||||
mind_wordmark.add_theme_font_override("font", Brand.font)
|
||||
mind_wordmark.add_theme_color_override("font_color", Brand.PURPLE)
|
||||
mind_wordmark.set_meta("brand_accent", true)
|
||||
wordmark.add_child(mind_wordmark)
|
||||
brand_tagline = RichTextLabel.new()
|
||||
brand_tagline.bbcode_enabled = true
|
||||
brand_tagline.fit_content = true
|
||||
brand_tagline.scroll_active = false
|
||||
brand_tagline.text = "Focused practice. Better decisions.\n[color=#2563eb]Step 3[/color] success."
|
||||
brand_tagline.add_theme_font_override("normal_font", Brand.font)
|
||||
brand_tagline.add_theme_font_size_override("normal_font_size", 20)
|
||||
brand_tagline.custom_minimum_size = Vector2(520, 64)
|
||||
brand_tagline.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
left_brand.add_child(brand_tagline)
|
||||
|
||||
var panel_center := CenterContainer.new()
|
||||
panel_center.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
panel_center.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
columns.add_child(panel_center)
|
||||
auth_panel = PanelContainer.new()
|
||||
auth_panel.custom_minimum_size = Vector2(610, 760)
|
||||
auth_panel.custom_minimum_size = Vector2(710, 820)
|
||||
panel_center.add_child(auth_panel)
|
||||
var scroll := ScrollContainer.new()
|
||||
scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||||
auth_panel.add_child(scroll)
|
||||
panel_content = VBoxContainer.new()
|
||||
panel_content.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
panel_content.add_theme_constant_override("separation", 16)
|
||||
panel_content.add_theme_constant_override("separation", 24)
|
||||
scroll.add_child(panel_content)
|
||||
|
||||
var panel_header := HBoxContainer.new()
|
||||
@@ -155,19 +173,20 @@ func _build_ui() -> void:
|
||||
panel_content.add_child(panel_header)
|
||||
var small_icon := TextureRect.new()
|
||||
small_icon.texture = preload("res://assets/branding/medmind-icon.png")
|
||||
small_icon.custom_minimum_size = Vector2(82, 82)
|
||||
small_icon.custom_minimum_size = Vector2(72, 72)
|
||||
small_icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||
small_icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||
small_icon.material = Brand.logo_cutout_material()
|
||||
panel_header.add_child(small_icon)
|
||||
var titles := VBoxContainer.new()
|
||||
titles.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
panel_header.add_child(titles)
|
||||
heading = Label.new()
|
||||
heading.add_theme_font_size_override("font_size", 34)
|
||||
heading.add_theme_font_size_override("font_size", 32)
|
||||
heading.add_theme_font_override("font", Brand.font)
|
||||
titles.add_child(heading)
|
||||
subheading = Label.new()
|
||||
subheading.add_theme_font_size_override("font_size", 18)
|
||||
subheading.add_theme_font_size_override("font_size", 20)
|
||||
subheading.add_theme_font_override("font", Brand.font)
|
||||
titles.add_child(subheading)
|
||||
|
||||
@@ -190,7 +209,7 @@ func _build_login_view() -> VBoxContainer:
|
||||
var view := _new_form()
|
||||
login_input = _add_field(view, "Email or Username", "Enter your email or username")
|
||||
password_input = _add_field(view, "Password", "Enter your password", true)
|
||||
_add_show_password(view, [password_input])
|
||||
_add_password_reveal(password_input)
|
||||
var forgot := Button.new()
|
||||
forgot.text = "Forgot password?"
|
||||
forgot.flat = true
|
||||
@@ -199,13 +218,10 @@ func _build_login_view() -> VBoxContainer:
|
||||
view.add_child(forgot)
|
||||
var sign_in := _add_primary_button(view, "Sign In", _on_login_pressed)
|
||||
password_input.text_submitted.connect(func(_text: String): _on_login_pressed())
|
||||
var divider := Label.new()
|
||||
divider.text = "or"
|
||||
divider.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
view.add_child(divider)
|
||||
view.add_child(_build_divider())
|
||||
var steam := Button.new()
|
||||
steam.text = "Sign in with Steam"
|
||||
steam.custom_minimum_size = Vector2(0, 58)
|
||||
steam.custom_minimum_size = Vector2(0, 68)
|
||||
steam.pressed.connect(_start_steam)
|
||||
view.add_child(steam)
|
||||
submit_buttons.append(steam)
|
||||
@@ -270,10 +286,26 @@ func _build_steam_view() -> VBoxContainer:
|
||||
|
||||
func _new_form() -> VBoxContainer:
|
||||
var form := VBoxContainer.new()
|
||||
form.add_theme_constant_override("separation", 12)
|
||||
form.add_theme_constant_override("separation", 16)
|
||||
return form
|
||||
|
||||
|
||||
func _build_divider() -> HBoxContainer:
|
||||
var row := HBoxContainer.new()
|
||||
row.add_theme_constant_override("separation", 22)
|
||||
for index in range(3):
|
||||
if index == 1:
|
||||
var label := Label.new()
|
||||
label.text = "or"
|
||||
label.add_theme_font_override("font", Brand.font)
|
||||
row.add_child(label)
|
||||
else:
|
||||
var line := HSeparator.new()
|
||||
line.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
row.add_child(line)
|
||||
return row
|
||||
|
||||
|
||||
func _add_field(parent: VBoxContainer, label_text: String, placeholder: String, secret := false) -> LineEdit:
|
||||
var label := Label.new()
|
||||
label.text = label_text
|
||||
@@ -283,8 +315,8 @@ func _add_field(parent: VBoxContainer, label_text: String, placeholder: String,
|
||||
var input := LineEdit.new()
|
||||
input.placeholder_text = placeholder
|
||||
input.secret = secret
|
||||
input.custom_minimum_size = Vector2(0, 54)
|
||||
input.add_theme_font_size_override("font_size", 16)
|
||||
input.custom_minimum_size = Vector2(0, 68)
|
||||
input.add_theme_font_size_override("font_size", 17)
|
||||
input.add_theme_font_override("font", Brand.font)
|
||||
parent.add_child(input)
|
||||
fields.append(input)
|
||||
@@ -294,17 +326,33 @@ func _add_field(parent: VBoxContainer, label_text: String, placeholder: String,
|
||||
func _add_show_password(parent: VBoxContainer, targets: Array) -> void:
|
||||
var toggle := CheckButton.new()
|
||||
toggle.text = "Show password"
|
||||
toggle.toggled.connect(func(show: bool):
|
||||
toggle.toggled.connect(func(should_reveal: bool):
|
||||
for target in targets:
|
||||
target.secret = not show
|
||||
target.secret = not should_reveal
|
||||
)
|
||||
parent.add_child(toggle)
|
||||
|
||||
|
||||
func _add_password_reveal(input: LineEdit) -> void:
|
||||
var reveal := Button.new()
|
||||
reveal.text = "\u25c9"
|
||||
reveal.tooltip_text = "Show password"
|
||||
reveal.flat = true
|
||||
reveal.focus_mode = Control.FOCUS_NONE
|
||||
reveal.custom_minimum_size = Vector2(52, 52)
|
||||
reveal.set_anchors_preset(Control.PRESET_CENTER_RIGHT)
|
||||
reveal.position = Vector2(-60, -26)
|
||||
reveal.pressed.connect(func():
|
||||
input.secret = not input.secret
|
||||
reveal.tooltip_text = "Show password" if input.secret else "Hide password"
|
||||
)
|
||||
input.add_child(reveal)
|
||||
|
||||
|
||||
func _add_primary_button(parent: VBoxContainer, text_value: String, callback: Callable) -> Button:
|
||||
var button := Button.new()
|
||||
button.text = text_value
|
||||
button.custom_minimum_size = Vector2(0, 58)
|
||||
button.custom_minimum_size = Vector2(0, 68)
|
||||
button.pressed.connect(callback)
|
||||
parent.add_child(button)
|
||||
submit_buttons.append(button)
|
||||
@@ -349,17 +397,28 @@ func _set_mode(next_mode: String) -> void:
|
||||
func _update_responsive_layout() -> void:
|
||||
if left_brand == null or layout_updating: return
|
||||
layout_updating = true
|
||||
var narrow := size.x < 1120.0
|
||||
var layout_width := size.x
|
||||
if layout_width <= 0.0:
|
||||
layout_width = get_viewport_rect().size.x
|
||||
var compact_desktop := layout_width < 900.0
|
||||
var mobile_web := false
|
||||
if OS.has_feature("web"):
|
||||
var browser_width: Variant = JavaScriptBridge.eval("window.innerWidth", true)
|
||||
narrow = float(browser_width) < 760.0
|
||||
left_brand.visible = not narrow
|
||||
compact_layout = narrow
|
||||
theme_button.custom_minimum_size = Vector2(160, 160) if narrow else Vector2(44, 44)
|
||||
theme_button.position = Vector2(-190, 20) if narrow else Vector2(-58, 14)
|
||||
theme_button.add_theme_font_size_override("font_size", 64 if narrow else 20)
|
||||
auth_panel.custom_minimum_size = Vector2(1700, 2300) if narrow else Vector2(610, 780)
|
||||
_apply_compact_scale(panel_content, 3.8 if narrow else 1.0)
|
||||
mobile_web = float(browser_width) < 760.0
|
||||
compact_layout = compact_desktop or mobile_web
|
||||
left_brand.visible = not compact_layout
|
||||
if mobile_web:
|
||||
theme_button.custom_minimum_size = Vector2(160, 160)
|
||||
theme_button.position = Vector2(-190, 20)
|
||||
theme_button.add_theme_font_size_override("font_size", 64)
|
||||
auth_panel.custom_minimum_size = Vector2(1700, 2300)
|
||||
_apply_compact_scale(panel_content, 3.8)
|
||||
else:
|
||||
theme_button.custom_minimum_size = Vector2(44, 44)
|
||||
theme_button.position = Vector2(-58, 14)
|
||||
theme_button.add_theme_font_size_override("font_size", 20)
|
||||
auth_panel.custom_minimum_size = Vector2(640, 790) if compact_desktop else Vector2(710, 820)
|
||||
_apply_compact_scale(panel_content, 1.0)
|
||||
_apply_theme()
|
||||
layout_updating = false
|
||||
|
||||
@@ -381,12 +440,17 @@ func _apply_compact_scale(control: Control, factor: float) -> void:
|
||||
func _apply_theme() -> void:
|
||||
var dark := UIThemeService.is_dark_mode()
|
||||
background.color = Brand.background_color(dark)
|
||||
background.material = Brand.background_material(dark)
|
||||
auth_panel.add_theme_stylebox_override("panel", Brand.panel_box(dark))
|
||||
theme_button.text = "☀" if dark else "☾"
|
||||
theme_button.tooltip_text = "Use light theme" if dark else "Use dark theme"
|
||||
theme_button.add_theme_font_override("font", Brand.font)
|
||||
for node in find_children("*", "Label", true, false):
|
||||
(node as Label).add_theme_color_override("font_color", Brand.text_color(dark))
|
||||
var label := node as Label
|
||||
if label.has_meta("brand_accent"):
|
||||
label.add_theme_color_override("font_color", Brand.PURPLE if dark else Brand.ROYAL_BLUE)
|
||||
else:
|
||||
label.add_theme_color_override("font_color", Brand.text_color(dark))
|
||||
for input in fields:
|
||||
input.add_theme_color_override("font_color", Brand.text_color(dark))
|
||||
input.add_theme_color_override("font_placeholder_color", Brand.muted_color(dark))
|
||||
@@ -403,8 +467,18 @@ func _apply_theme() -> void:
|
||||
if button.text not in ["Sign in with Steam"]:
|
||||
button.add_theme_stylebox_override("normal", Brand.gradient_button_box())
|
||||
button.add_theme_color_override("font_color", Color.WHITE)
|
||||
var theme_normal := StyleBoxFlat.new()
|
||||
theme_normal.bg_color = Color("111b30") if dark else Brand.LIGHT_SURFACE
|
||||
theme_normal.border_color = Color("33415d") if dark else Brand.LIGHT_BORDER
|
||||
theme_normal.set_border_width_all(1)
|
||||
theme_normal.set_corner_radius_all(22)
|
||||
theme_button.add_theme_stylebox_override("normal", theme_normal)
|
||||
theme_button.add_theme_stylebox_override("hover", theme_normal)
|
||||
theme_button.add_theme_color_override("font_color", Brand.AMBER)
|
||||
subheading.add_theme_color_override("font_color", Brand.muted_color(dark))
|
||||
message_label.add_theme_color_override("font_color", Brand.muted_color(dark))
|
||||
if brand_tagline != null:
|
||||
brand_tagline.add_theme_color_override("default_color", Brand.muted_color(dark))
|
||||
|
||||
|
||||
func _on_theme_changed(_is_dark: bool) -> void:
|
||||
|
||||
Reference in New Issue
Block a user