feature updates
This commit is contained in:
431
scripts/ui/RegisterScreen.gd
Normal file
431
scripts/ui/RegisterScreen.gd
Normal file
@@ -0,0 +1,431 @@
|
||||
class_name RegisterScreen
|
||||
extends CanvasLayer
|
||||
|
||||
## RegisterScreen - Account creation form for online play
|
||||
|
||||
signal registration_successful(message: String)
|
||||
signal login_requested
|
||||
signal back_pressed
|
||||
|
||||
const WINDOW_SIZE := Vector2(400, 600)
|
||||
|
||||
# Validation constants
|
||||
const USERNAME_MIN_LENGTH = 3
|
||||
const USERNAME_MAX_LENGTH = 32
|
||||
const PASSWORD_MIN_LENGTH = 8
|
||||
|
||||
# UI Components
|
||||
var background: PanelContainer
|
||||
var main_vbox: VBoxContainer
|
||||
var title_label: Label
|
||||
var email_input: LineEdit
|
||||
var username_input: LineEdit
|
||||
var password_input: LineEdit
|
||||
var confirm_password_input: LineEdit
|
||||
var register_button: Button
|
||||
var login_link: Button
|
||||
var back_button: Button
|
||||
var error_label: Label
|
||||
var success_label: Label
|
||||
|
||||
# State
|
||||
var _is_loading: bool = false
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
layer = 100
|
||||
_create_ui()
|
||||
|
||||
|
||||
func _create_ui() -> void:
|
||||
# Background panel
|
||||
background = PanelContainer.new()
|
||||
add_child(background)
|
||||
background.position = Vector2.ZERO
|
||||
background.size = WINDOW_SIZE
|
||||
background.add_theme_stylebox_override("panel", _create_panel_style())
|
||||
|
||||
# Main layout with margin
|
||||
var margin = MarginContainer.new()
|
||||
background.add_child(margin)
|
||||
margin.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
margin.add_theme_constant_override("margin_left", 40)
|
||||
margin.add_theme_constant_override("margin_right", 40)
|
||||
margin.add_theme_constant_override("margin_top", 25)
|
||||
margin.add_theme_constant_override("margin_bottom", 25)
|
||||
|
||||
main_vbox = VBoxContainer.new()
|
||||
margin.add_child(main_vbox)
|
||||
main_vbox.add_theme_constant_override("separation", 10)
|
||||
|
||||
# Title
|
||||
_create_title()
|
||||
|
||||
# Registration form
|
||||
_create_form()
|
||||
|
||||
# Messages
|
||||
_create_message_labels()
|
||||
|
||||
# Spacer
|
||||
var spacer = Control.new()
|
||||
spacer.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
main_vbox.add_child(spacer)
|
||||
|
||||
# Links
|
||||
_create_links()
|
||||
|
||||
# Back button
|
||||
_create_back_button()
|
||||
|
||||
|
||||
func _create_title() -> void:
|
||||
title_label = Label.new()
|
||||
title_label.text = "CREATE ACCOUNT"
|
||||
title_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
title_label.add_theme_font_size_override("font_size", 28)
|
||||
title_label.add_theme_color_override("font_color", Color(1.0, 0.95, 0.8))
|
||||
main_vbox.add_child(title_label)
|
||||
|
||||
# Separator
|
||||
var separator = HSeparator.new()
|
||||
separator.add_theme_stylebox_override("separator", _create_separator_style())
|
||||
main_vbox.add_child(separator)
|
||||
|
||||
# Small spacer
|
||||
var spacer = Control.new()
|
||||
spacer.custom_minimum_size.y = 10
|
||||
main_vbox.add_child(spacer)
|
||||
|
||||
|
||||
func _create_form() -> void:
|
||||
# Email field
|
||||
var email_label = Label.new()
|
||||
email_label.text = "Email"
|
||||
email_label.add_theme_font_size_override("font_size", 15)
|
||||
email_label.add_theme_color_override("font_color", Color(0.9, 0.85, 0.7))
|
||||
main_vbox.add_child(email_label)
|
||||
|
||||
email_input = LineEdit.new()
|
||||
email_input.placeholder_text = "Enter your email"
|
||||
email_input.custom_minimum_size = Vector2(0, 38)
|
||||
_style_input(email_input)
|
||||
main_vbox.add_child(email_input)
|
||||
|
||||
# Username field
|
||||
var username_label = Label.new()
|
||||
username_label.text = "Username"
|
||||
username_label.add_theme_font_size_override("font_size", 15)
|
||||
username_label.add_theme_color_override("font_color", Color(0.9, 0.85, 0.7))
|
||||
main_vbox.add_child(username_label)
|
||||
|
||||
username_input = LineEdit.new()
|
||||
username_input.placeholder_text = "3-32 characters"
|
||||
username_input.custom_minimum_size = Vector2(0, 38)
|
||||
username_input.max_length = USERNAME_MAX_LENGTH
|
||||
_style_input(username_input)
|
||||
main_vbox.add_child(username_input)
|
||||
|
||||
# Password field
|
||||
var password_label = Label.new()
|
||||
password_label.text = "Password"
|
||||
password_label.add_theme_font_size_override("font_size", 15)
|
||||
password_label.add_theme_color_override("font_color", Color(0.9, 0.85, 0.7))
|
||||
main_vbox.add_child(password_label)
|
||||
|
||||
password_input = LineEdit.new()
|
||||
password_input.placeholder_text = "At least 8 characters"
|
||||
password_input.secret = true
|
||||
password_input.custom_minimum_size = Vector2(0, 38)
|
||||
_style_input(password_input)
|
||||
main_vbox.add_child(password_input)
|
||||
|
||||
# Confirm password field
|
||||
var confirm_label = Label.new()
|
||||
confirm_label.text = "Confirm Password"
|
||||
confirm_label.add_theme_font_size_override("font_size", 15)
|
||||
confirm_label.add_theme_color_override("font_color", Color(0.9, 0.85, 0.7))
|
||||
main_vbox.add_child(confirm_label)
|
||||
|
||||
confirm_password_input = LineEdit.new()
|
||||
confirm_password_input.placeholder_text = "Re-enter your password"
|
||||
confirm_password_input.secret = true
|
||||
confirm_password_input.custom_minimum_size = Vector2(0, 38)
|
||||
_style_input(confirm_password_input)
|
||||
confirm_password_input.text_submitted.connect(_on_input_submitted)
|
||||
main_vbox.add_child(confirm_password_input)
|
||||
|
||||
# Register button
|
||||
var button_spacer = Control.new()
|
||||
button_spacer.custom_minimum_size.y = 10
|
||||
main_vbox.add_child(button_spacer)
|
||||
|
||||
register_button = Button.new()
|
||||
register_button.text = "Create Account"
|
||||
register_button.custom_minimum_size = Vector2(0, 45)
|
||||
_style_button(register_button, true)
|
||||
register_button.pressed.connect(_on_register_pressed)
|
||||
main_vbox.add_child(register_button)
|
||||
|
||||
|
||||
func _create_message_labels() -> void:
|
||||
# Error label
|
||||
error_label = Label.new()
|
||||
error_label.text = ""
|
||||
error_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
error_label.add_theme_font_size_override("font_size", 13)
|
||||
error_label.add_theme_color_override("font_color", Color(1.0, 0.4, 0.4))
|
||||
error_label.autowrap_mode = TextServer.AUTOWRAP_WORD
|
||||
error_label.visible = false
|
||||
main_vbox.add_child(error_label)
|
||||
|
||||
# Success label
|
||||
success_label = Label.new()
|
||||
success_label.text = ""
|
||||
success_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
success_label.add_theme_font_size_override("font_size", 13)
|
||||
success_label.add_theme_color_override("font_color", Color(0.4, 1.0, 0.5))
|
||||
success_label.autowrap_mode = TextServer.AUTOWRAP_WORD
|
||||
success_label.visible = false
|
||||
main_vbox.add_child(success_label)
|
||||
|
||||
|
||||
func _create_links() -> void:
|
||||
# Login link
|
||||
login_link = Button.new()
|
||||
login_link.text = "Already have an account? Login"
|
||||
login_link.flat = true
|
||||
login_link.add_theme_font_size_override("font_size", 14)
|
||||
login_link.add_theme_color_override("font_color", Color(0.6, 0.7, 1.0))
|
||||
login_link.add_theme_color_override("font_hover_color", Color(0.8, 0.85, 1.0))
|
||||
login_link.pressed.connect(_on_login_pressed)
|
||||
main_vbox.add_child(login_link)
|
||||
|
||||
|
||||
func _create_back_button() -> void:
|
||||
var button_container = HBoxContainer.new()
|
||||
button_container.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
main_vbox.add_child(button_container)
|
||||
|
||||
back_button = Button.new()
|
||||
back_button.text = "Back"
|
||||
back_button.custom_minimum_size = Vector2(100, 38)
|
||||
_style_button(back_button, false)
|
||||
back_button.pressed.connect(_on_back_pressed)
|
||||
button_container.add_child(back_button)
|
||||
|
||||
|
||||
# ======= STYLING =======
|
||||
|
||||
func _create_panel_style() -> StyleBoxFlat:
|
||||
var style = StyleBoxFlat.new()
|
||||
style.bg_color = Color(0.08, 0.08, 0.12, 1.0)
|
||||
style.set_border_width_all(0)
|
||||
style.set_corner_radius_all(0)
|
||||
return style
|
||||
|
||||
|
||||
func _create_separator_style() -> StyleBoxFlat:
|
||||
var style = StyleBoxFlat.new()
|
||||
style.bg_color = Color(0.5, 0.4, 0.2, 0.5)
|
||||
style.content_margin_top = 1
|
||||
return style
|
||||
|
||||
|
||||
func _style_input(input: LineEdit) -> void:
|
||||
var style = StyleBoxFlat.new()
|
||||
style.bg_color = Color(0.12, 0.12, 0.16)
|
||||
style.border_color = Color(0.4, 0.35, 0.25)
|
||||
style.set_border_width_all(1)
|
||||
style.set_corner_radius_all(4)
|
||||
style.content_margin_left = 12
|
||||
style.content_margin_right = 12
|
||||
style.content_margin_top = 6
|
||||
style.content_margin_bottom = 6
|
||||
|
||||
input.add_theme_stylebox_override("normal", style)
|
||||
|
||||
var focus_style = style.duplicate()
|
||||
focus_style.border_color = Color(0.7, 0.6, 0.3)
|
||||
input.add_theme_stylebox_override("focus", focus_style)
|
||||
|
||||
input.add_theme_color_override("font_color", Color(0.95, 0.9, 0.8))
|
||||
input.add_theme_color_override("font_placeholder_color", Color(0.5, 0.5, 0.55))
|
||||
input.add_theme_font_size_override("font_size", 15)
|
||||
|
||||
|
||||
func _style_button(button: Button, is_primary: bool) -> void:
|
||||
var style = StyleBoxFlat.new()
|
||||
if is_primary:
|
||||
style.bg_color = Color(0.3, 0.25, 0.15)
|
||||
style.border_color = Color(0.6, 0.5, 0.3)
|
||||
else:
|
||||
style.bg_color = Color(0.15, 0.15, 0.2)
|
||||
style.border_color = Color(0.4, 0.35, 0.25)
|
||||
style.set_border_width_all(2)
|
||||
style.set_corner_radius_all(6)
|
||||
style.content_margin_left = 20
|
||||
style.content_margin_right = 20
|
||||
style.content_margin_top = 8
|
||||
style.content_margin_bottom = 8
|
||||
|
||||
button.add_theme_stylebox_override("normal", style)
|
||||
|
||||
var hover_style = style.duplicate()
|
||||
if is_primary:
|
||||
hover_style.bg_color = Color(0.4, 0.35, 0.2)
|
||||
hover_style.border_color = Color(0.8, 0.7, 0.4)
|
||||
else:
|
||||
hover_style.bg_color = Color(0.2, 0.2, 0.25)
|
||||
hover_style.border_color = Color(0.5, 0.45, 0.35)
|
||||
button.add_theme_stylebox_override("hover", hover_style)
|
||||
|
||||
var pressed_style = style.duplicate()
|
||||
pressed_style.bg_color = Color(0.1, 0.1, 0.12)
|
||||
button.add_theme_stylebox_override("pressed", pressed_style)
|
||||
|
||||
var disabled_style = style.duplicate()
|
||||
disabled_style.bg_color = Color(0.1, 0.1, 0.12)
|
||||
disabled_style.border_color = Color(0.25, 0.25, 0.3)
|
||||
button.add_theme_stylebox_override("disabled", disabled_style)
|
||||
|
||||
button.add_theme_color_override("font_color", Color(0.9, 0.85, 0.7))
|
||||
button.add_theme_color_override("font_hover_color", Color(1.0, 0.95, 0.8))
|
||||
button.add_theme_color_override("font_pressed_color", Color(0.7, 0.65, 0.55))
|
||||
button.add_theme_color_override("font_disabled_color", Color(0.45, 0.42, 0.38))
|
||||
button.add_theme_font_size_override("font_size", 16)
|
||||
|
||||
|
||||
# ======= EVENT HANDLERS =======
|
||||
|
||||
func _on_input_submitted(_text: String) -> void:
|
||||
_on_register_pressed()
|
||||
|
||||
|
||||
func _on_register_pressed() -> void:
|
||||
if _is_loading:
|
||||
return
|
||||
|
||||
var email = email_input.text.strip_edges()
|
||||
var username = username_input.text.strip_edges()
|
||||
var password = password_input.text
|
||||
var confirm_password = confirm_password_input.text
|
||||
|
||||
# Validate inputs
|
||||
var validation_error = _validate_inputs(email, username, password, confirm_password)
|
||||
if validation_error != "":
|
||||
_show_error(validation_error)
|
||||
return
|
||||
|
||||
# Start registration
|
||||
_set_loading(true)
|
||||
_hide_messages()
|
||||
|
||||
var result = await NetworkManager.register(email, password, username)
|
||||
|
||||
_set_loading(false)
|
||||
|
||||
if result.success:
|
||||
_show_success(result.message)
|
||||
registration_successful.emit(result.message)
|
||||
else:
|
||||
_show_error(result.message)
|
||||
|
||||
|
||||
func _on_login_pressed() -> void:
|
||||
login_requested.emit()
|
||||
|
||||
|
||||
func _on_back_pressed() -> void:
|
||||
back_pressed.emit()
|
||||
|
||||
|
||||
# ======= VALIDATION =======
|
||||
|
||||
func _validate_inputs(email: String, username: String, password: String, confirm_password: String) -> String:
|
||||
# Email validation
|
||||
if email.is_empty():
|
||||
return "Please enter your email"
|
||||
|
||||
if not _is_valid_email(email):
|
||||
return "Please enter a valid email address"
|
||||
|
||||
# Username validation
|
||||
if username.is_empty():
|
||||
return "Please enter a username"
|
||||
|
||||
if username.length() < USERNAME_MIN_LENGTH:
|
||||
return "Username must be at least %d characters" % USERNAME_MIN_LENGTH
|
||||
|
||||
if username.length() > USERNAME_MAX_LENGTH:
|
||||
return "Username must be at most %d characters" % USERNAME_MAX_LENGTH
|
||||
|
||||
if not _is_valid_username(username):
|
||||
return "Username can only contain letters, numbers, underscores, and hyphens"
|
||||
|
||||
# Password validation
|
||||
if password.is_empty():
|
||||
return "Please enter a password"
|
||||
|
||||
if password.length() < PASSWORD_MIN_LENGTH:
|
||||
return "Password must be at least %d characters" % PASSWORD_MIN_LENGTH
|
||||
|
||||
# Confirm password
|
||||
if confirm_password != password:
|
||||
return "Passwords do not match"
|
||||
|
||||
return ""
|
||||
|
||||
|
||||
func _is_valid_email(email: String) -> bool:
|
||||
var regex = RegEx.new()
|
||||
regex.compile("^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$")
|
||||
return regex.search(email) != null
|
||||
|
||||
|
||||
func _is_valid_username(username: String) -> bool:
|
||||
var regex = RegEx.new()
|
||||
regex.compile("^[a-zA-Z0-9_-]+$")
|
||||
return regex.search(username) != null
|
||||
|
||||
|
||||
# ======= HELPERS =======
|
||||
|
||||
func _show_error(message: String) -> void:
|
||||
error_label.text = message
|
||||
error_label.visible = true
|
||||
success_label.visible = false
|
||||
|
||||
|
||||
func _show_success(message: String) -> void:
|
||||
success_label.text = message
|
||||
success_label.visible = true
|
||||
error_label.visible = false
|
||||
|
||||
|
||||
func _hide_messages() -> void:
|
||||
error_label.visible = false
|
||||
success_label.visible = false
|
||||
|
||||
|
||||
func _set_loading(loading: bool) -> void:
|
||||
_is_loading = loading
|
||||
register_button.disabled = loading
|
||||
register_button.text = "Creating Account..." if loading else "Create Account"
|
||||
email_input.editable = not loading
|
||||
username_input.editable = not loading
|
||||
password_input.editable = not loading
|
||||
confirm_password_input.editable = not loading
|
||||
|
||||
|
||||
func clear_form() -> void:
|
||||
email_input.text = ""
|
||||
username_input.text = ""
|
||||
password_input.text = ""
|
||||
confirm_password_input.text = ""
|
||||
_hide_messages()
|
||||
_set_loading(false)
|
||||
|
||||
|
||||
func focus_email() -> void:
|
||||
email_input.grab_focus()
|
||||
Reference in New Issue
Block a user