// package screens implements application screens (login, main menu, settings). package screens import ( "fmt" "fyne.io/fyne/v2" "fyne.io/fyne/v2/container" "fyne.io/fyne/v2/dialog" "fyne.io/fyne/v2/layout" "fyne.io/fyne/v2/widget" "gitea.mrixs.me/Mrixs/MrixsCraft-launcher/internal/auth" "gitea.mrixs.me/Mrixs/MrixsCraft-launcher/internal/ui/components" ) // Modpack represents a single server/modpack entry from the backend. type Modpack struct { Slug string Name string } // MainScreen builds the primary launcher window content. func MainScreen( w fyne.Window, client *auth.Client, session *auth.Session, serverList []Modpack, onPlay func(), onSettings func(), ) fyne.CanvasObject { // ── Sidebar (Left) ────────────────────────────────────── serverLabels := make([]*widget.Label, len(serverList)) serverContainer := container.NewVBox() for i, sp := range serverList { label := widget.NewLabel(sp.Name) serverLabels[i] = label index := i card := components.ServerCard(sp.Name, index == 0, func() { for j, l := range serverLabels { l.TextStyle.Bold = (j == index) l.Refresh() } }) serverContainer.Add(card) } sidebar := container.NewVScroll(serverContainer) // ── Center (background + info) ────────────────────────── bg := widget.NewRichTextFromMarkdown( "# Welcome to MrixsCraft\n\n" + "Select a server from the left panel and press **PLAY**.\n\n" + "---\n\n" + "*Version: dev*", ) center := container.NewMax(widget.NewCard("", "", bg)) // ── Bottom bar ────────────────────────────────────────── avatar := components.AvatarImage() displayName := "Not logged in" if session != nil && session.Username != "" { displayName = session.Username } usernameLabel := widget.NewLabel(displayName) logoutBtn := components.LogoutButton(func() { dialog.ShowConfirm( "Logout", "Are you sure you want to log out?", func(confirmed bool) { if confirmed { _ = auth.Delete() w.Close() } }, w, ) }) bottomLeft := container.NewHBox( avatar, usernameLabel, layout.NewSpacer(), logoutBtn, ) progress := components.NewProgressBar() playBtn := components.PlayButton(onPlay) settingsBtn := components.SettingsButton(onSettings) bottomRight := container.NewHBox( settingsBtn, widget.NewSeparator(), playBtn, ) bottomCenter := container.NewMax(progress) bottom := container.NewBorder( nil, nil, bottomLeft, bottomRight, bottomCenter, ) // ── Assemble ──────────────────────────────────────────── return container.NewBorder( nil, bottom, sidebar, nil, center, ) } // LoginScreen builds a modal login form. func LoginScreen(w fyne.Window, client *auth.Client, onLogin func(*auth.Session)) { username := widget.NewEntry() username.SetPlaceHolder("Username or email") password := widget.NewPasswordEntry() password.SetPlaceHolder("Password") form := widget.NewForm( widget.NewFormItem("Username", username), widget.NewFormItem("Password", password), ) dialog.ShowCustomConfirm( "Login — MrixsCraft", "Login", "Cancel", form, func(confirmed bool) { if !confirmed { return } sess, err := client.Authenticate(username.Text, password.Text) if err != nil { dialog.ShowError(err, w) return } if err := sess.Save(); err != nil { dialog.ShowError(err, w) return } onLogin(sess) }, w, ) } // SettingsScreen builds the settings modal. func SettingsScreen(w fyne.Window, onSave func(memoryMB int, extraArgs string)) { memorySlider := widget.NewSlider(1024, 16384) memorySlider.Step = 512 memorySlider.Value = 4096 memLabel := widget.NewLabel("4096 MB") memorySlider.OnChanged = func(v float64) { memLabel.SetText(fmt.Sprintf("%.0f MB", v)) } extraArgs := widget.NewMultiLineEntry() extraArgs.SetPlaceHolder("e.g. -XX:+UseG1GC -XX:MaxGCPauseMillis=50") content := container.NewVBox( widget.NewLabelWithStyle("Memory (RAM)", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}), memLabel, memorySlider, widget.NewSeparator(), widget.NewLabelWithStyle("Extra JVM arguments", fyne.TextAlignLeading, fyne.TextStyle{}), extraArgs, ) dialog.ShowCustomConfirm( "Settings", "Save", "Cancel", content, func(confirmed bool) { if confirmed { onSave(int(memorySlider.Value), extraArgs.Text) } }, w, ) }