2024-12-20 18:58:53 +01:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
rl "github.com/gen2brain/raylib-go/raylib"
|
|
|
|
)
|
|
|
|
|
2024-12-25 08:46:25 +01:00
|
|
|
// Keep your global variables for steps and transitions
|
|
|
|
var (
|
|
|
|
transition = NewTransitionManager()
|
|
|
|
currentStep = 0
|
|
|
|
targetStep = 0
|
|
|
|
useDefault = false
|
2024-12-20 18:58:53 +01:00
|
|
|
|
2024-12-25 08:46:25 +01:00
|
|
|
step1DefaultRect rl.Rectangle
|
|
|
|
step1CustomRect rl.Rectangle
|
|
|
|
|
|
|
|
// Our global Installer from installer.go
|
|
|
|
installer = NewInstaller()
|
|
|
|
)
|
2024-12-20 18:58:53 +01:00
|
|
|
|
|
|
|
func main() {
|
|
|
|
monitor := rl.GetCurrentMonitor()
|
|
|
|
screenW := rl.GetMonitorWidth(monitor)
|
|
|
|
screenH := rl.GetMonitorHeight(monitor)
|
|
|
|
|
|
|
|
rl.InitWindow(int32(screenW), int32(screenH), "Spitfire Browser Installer")
|
|
|
|
rl.SetWindowState(rl.FlagFullscreenMode)
|
|
|
|
defer rl.CloseWindow()
|
|
|
|
|
2024-12-21 09:33:30 +01:00
|
|
|
refreshRate := rl.GetMonitorRefreshRate(monitor)
|
|
|
|
if refreshRate <= 0 {
|
|
|
|
refreshRate = 60 // Fallback to 60 if detection fails
|
|
|
|
}
|
|
|
|
rl.SetTargetFPS(int32(refreshRate))
|
2024-12-20 18:58:53 +01:00
|
|
|
|
|
|
|
InitBackground(rl.GetScreenWidth(), rl.GetScreenHeight())
|
|
|
|
|
2024-12-25 08:46:25 +01:00
|
|
|
// Start the download+decompress in background immediately:
|
|
|
|
installer.StartDownloadDecompress()
|
|
|
|
|
2024-12-20 18:58:53 +01:00
|
|
|
targetStep = 0
|
|
|
|
|
|
|
|
for !rl.WindowShouldClose() {
|
|
|
|
screenW = rl.GetScreenWidth()
|
|
|
|
screenH = rl.GetScreenHeight()
|
|
|
|
mousePos := rl.GetMousePosition()
|
|
|
|
|
|
|
|
buttonW := 100
|
|
|
|
buttonH := 30
|
|
|
|
prevX := int32(50)
|
|
|
|
prevY := int32(screenH - 50)
|
|
|
|
nextX := int32(screenW - 150)
|
|
|
|
nextY := prevY
|
|
|
|
|
2024-12-25 08:46:25 +01:00
|
|
|
// Update transition
|
2024-12-20 18:58:53 +01:00
|
|
|
oldAlpha, oldScale, oldOffsetX, newAlpha, newScale, newOffsetX := transition.Update()
|
|
|
|
|
|
|
|
if !transition.IsActive() && currentStep != targetStep {
|
|
|
|
currentStep = targetStep
|
|
|
|
}
|
|
|
|
|
2024-12-25 08:46:25 +01:00
|
|
|
// Poll SPM progress for the GUI
|
|
|
|
installer.PollProgress()
|
|
|
|
|
|
|
|
// If an unrecoverable error occurred, you could handle it here:
|
|
|
|
// (For now, we just print it in the console.)
|
|
|
|
if installer.LastError != nil {
|
|
|
|
fmt.Println("SPM Error:", installer.LastError)
|
|
|
|
// You might choose to show a popup or do something else
|
|
|
|
}
|
|
|
|
|
|
|
|
// GUI input
|
2024-12-20 18:58:53 +01:00
|
|
|
if !transition.IsActive() && rl.IsMouseButtonPressed(rl.MouseLeftButton) {
|
|
|
|
handleInput(mousePos, screenW, screenH, buttonW, buttonH, prevX, prevY, nextX, nextY)
|
|
|
|
}
|
|
|
|
|
|
|
|
UpdateBackground(screenW, screenH)
|
|
|
|
|
|
|
|
rl.BeginDrawing()
|
|
|
|
DrawBackground(screenW, screenH)
|
|
|
|
|
|
|
|
drawHeader(screenW)
|
|
|
|
|
|
|
|
oldStep := currentStep
|
|
|
|
newStep := currentStep
|
|
|
|
if transition.IsActive() {
|
|
|
|
oldStep = transition.oldStep
|
|
|
|
newStep = transition.newStep
|
|
|
|
}
|
|
|
|
|
|
|
|
phase := transition.GetPhase()
|
|
|
|
if transition.IsActive() {
|
|
|
|
if phase == TransitionOutFadeScale {
|
|
|
|
drawStep(oldStep, screenW, screenH, mousePos, oldAlpha, oldScale, oldOffsetX)
|
|
|
|
} else {
|
|
|
|
drawStep(oldStep, screenW, screenH, mousePos, oldAlpha, oldScale, oldOffsetX)
|
|
|
|
drawStep(newStep, screenW, screenH, mousePos, newAlpha, newScale, newOffsetX)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
drawStep(currentStep, screenW, screenH, mousePos, 1.0, 1.0, 0.0)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !transition.IsActive() {
|
|
|
|
if currentStep > 0 && currentStep < 3 {
|
|
|
|
drawButton("Previous", prevX, prevY, int32(buttonW), int32(buttonH), mousePos)
|
|
|
|
}
|
|
|
|
if currentStep == 1 {
|
|
|
|
drawButton("Next", nextX, nextY, int32(buttonW), int32(buttonH), mousePos)
|
|
|
|
} else if currentStep == 2 {
|
|
|
|
drawButton("Finish", nextX, nextY, int32(buttonW), int32(buttonH), mousePos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-25 08:46:25 +01:00
|
|
|
// Draw the semi-transparent loading circle if user is downloading or installing
|
|
|
|
if installer.IsDownloading || installer.IsInstalling {
|
|
|
|
drawInstallProgress(screenW, installer.Progress, installer.Task)
|
|
|
|
}
|
|
|
|
|
2024-12-20 18:58:53 +01:00
|
|
|
rl.EndDrawing()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-25 08:46:25 +01:00
|
|
|
// handleInput acts on mouse clicks in each step
|
|
|
|
func handleInput(mousePos rl.Vector2, screenW, screenH, buttonW, buttonH int,
|
|
|
|
prevX, prevY, nextX, nextY int32) {
|
2024-12-20 18:58:53 +01:00
|
|
|
|
|
|
|
if currentStep == 0 {
|
|
|
|
if overRect(mousePos, step1DefaultRect) {
|
2024-12-25 08:46:25 +01:00
|
|
|
// user clicked "Default" => do final install if not already installing
|
|
|
|
useDefault = true
|
|
|
|
if !installer.IsInstalling && !installer.DoneInstall {
|
|
|
|
installer.FinalInstall()
|
|
|
|
}
|
|
|
|
fmt.Println("Installation started with default settings.")
|
2024-12-20 18:58:53 +01:00
|
|
|
}
|
|
|
|
if overRect(mousePos, step1CustomRect) {
|
|
|
|
useDefault = false
|
|
|
|
startTransition(currentStep, 1)
|
|
|
|
}
|
|
|
|
} else if currentStep == 1 {
|
|
|
|
if overButton(mousePos, nextX, nextY, int32(buttonW), int32(buttonH)) {
|
|
|
|
startTransition(currentStep, 2)
|
|
|
|
}
|
|
|
|
if overButton(mousePos, int32(prevX), int32(prevY), int32(buttonW), int32(buttonH)) {
|
|
|
|
startTransition(currentStep, 0)
|
|
|
|
}
|
|
|
|
selectColor(mousePos)
|
|
|
|
selectContrastIcon(mousePos)
|
|
|
|
selectTheme(mousePos)
|
|
|
|
} else if currentStep == 2 {
|
|
|
|
if overButton(mousePos, nextX, nextY, int32(buttonW), int32(buttonH)) {
|
2024-12-25 08:46:25 +01:00
|
|
|
// user clicked "Finish" => final install if not already installing
|
|
|
|
if !installer.IsInstalling && !installer.DoneInstall {
|
|
|
|
installer.FinalInstall()
|
|
|
|
}
|
|
|
|
fmt.Printf("Installation started:\nDefault: %v\nColor: %s\nTheme: %s\nLayout: %s\n",
|
2024-12-20 18:58:53 +01:00
|
|
|
useDefault, selectedColor, selectedTheme, selectedLayout)
|
|
|
|
}
|
|
|
|
if overButton(mousePos, int32(prevX), int32(prevY), int32(buttonW), int32(buttonH)) {
|
|
|
|
startTransition(currentStep, 1)
|
|
|
|
}
|
|
|
|
selectLayoutOption(mousePos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-25 08:46:25 +01:00
|
|
|
// drawHeader is your original function
|
2024-12-20 18:58:53 +01:00
|
|
|
func drawHeader(screenW int) {
|
|
|
|
title := "Spitfire Browser Installer"
|
|
|
|
titleFontSize := int32(30)
|
|
|
|
titleWidth := rl.MeasureText(title, titleFontSize)
|
|
|
|
rl.DrawText(title, (int32(screenW)-titleWidth)/2, 20, titleFontSize, rl.RayWhite)
|
|
|
|
rl.DrawLine(50, 60, int32(screenW)-50, 60, rl.Fade(rl.White, 0.5))
|
|
|
|
}
|
2024-12-25 08:46:25 +01:00
|
|
|
|
|
|
|
// drawInstallProgress displays a simple white circle with partial alpha,
|
|
|
|
// plus the current task text below it, in the top-right area.
|
|
|
|
func drawInstallProgress(screenW int, progress int, task string) {
|
|
|
|
circleX := float32(screenW - 80)
|
|
|
|
circleY := float32(100)
|
|
|
|
radius := float32(30)
|
|
|
|
|
|
|
|
// Colors for the circle
|
|
|
|
bgColor := rl.Color{R: 255, G: 255, B: 255, A: 80}
|
|
|
|
fillColor := rl.Color{R: 255, G: 255, B: 255, A: 200}
|
|
|
|
|
|
|
|
// Draw background circle
|
|
|
|
rl.DrawCircle(int32(circleX), int32(circleY), radius, bgColor)
|
|
|
|
|
|
|
|
// Draw progress arc
|
|
|
|
angle := float32(progress) / 100.0 * 360.0
|
|
|
|
rl.DrawCircleSector(
|
|
|
|
rl.Vector2{X: circleX, Y: circleY},
|
|
|
|
radius,
|
|
|
|
0,
|
|
|
|
angle,
|
|
|
|
40,
|
|
|
|
fillColor,
|
|
|
|
)
|
|
|
|
|
|
|
|
// Print numeric progress
|
|
|
|
txt := fmt.Sprintf("%3d%%", progress)
|
|
|
|
rl.DrawText(txt, int32(circleX)-rl.MeasureText(txt, 18)/2, int32(circleY)-10, 18, rl.White)
|
|
|
|
|
|
|
|
// Draw wrapped task text below the circle
|
|
|
|
drawTextWrapped(task, int32(circleX)-100, int32(circleY)+40, 200, 18, rl.White)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Custom function to draw text with wrapping
|
|
|
|
func drawTextWrapped(text string, x, y int32, maxWidth, fontSize int32, color rl.Color) {
|
|
|
|
words := splitIntoWords(text)
|
|
|
|
line := ""
|
|
|
|
offsetY := int32(0)
|
|
|
|
|
|
|
|
for _, word := range words {
|
|
|
|
testLine := line + word + " "
|
|
|
|
if rl.MeasureText(testLine, fontSize) > int32(maxWidth) {
|
|
|
|
rl.DrawText(line, x, y+offsetY, fontSize, color)
|
|
|
|
line = word + " "
|
|
|
|
offsetY += fontSize + 2
|
|
|
|
} else {
|
|
|
|
line = testLine
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw the last line
|
|
|
|
if line != "" {
|
|
|
|
rl.DrawText(line, x, y+offsetY, fontSize, color)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Helper function to split text into words
|
|
|
|
func splitIntoWords(text string) []string {
|
|
|
|
words := []string{}
|
|
|
|
word := ""
|
|
|
|
for _, char := range text {
|
|
|
|
if char == ' ' || char == '\n' {
|
|
|
|
if word != "" {
|
|
|
|
words = append(words, word)
|
|
|
|
word = ""
|
|
|
|
}
|
|
|
|
if char == '\n' {
|
|
|
|
words = append(words, "\n")
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
word += string(char)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if word != "" {
|
|
|
|
words = append(words, word)
|
|
|
|
}
|
|
|
|
return words
|
|
|
|
}
|
|
|
|
|
|
|
|
// startTransition is your existing function
|
|
|
|
func startTransition(from, to int) {
|
|
|
|
targetStep = to
|
|
|
|
transition.Start(from, to)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SPM example
|
|
|
|
|
|
|
|
// package main
|
|
|
|
|
|
|
|
// import (
|
|
|
|
// "fmt"
|
|
|
|
// "os"
|
|
|
|
// "path/filepath"
|
|
|
|
// "spitfire-installer/spm"
|
|
|
|
// "time"
|
|
|
|
// )
|
|
|
|
|
|
|
|
// func main() {
|
|
|
|
// // Start a goroutine to display progress updates
|
|
|
|
// done := make(chan bool)
|
|
|
|
// go func() {
|
|
|
|
// for {
|
|
|
|
// select {
|
|
|
|
// case <-done:
|
|
|
|
// return
|
|
|
|
// default:
|
|
|
|
// percentage, task := spm.GetProgress()
|
|
|
|
// fmt.Printf("\r[%3d%%] %s", percentage, task)
|
|
|
|
// time.Sleep(500 * time.Millisecond)
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }()
|
|
|
|
|
|
|
|
// // Set up the download directory
|
|
|
|
// downloadDir := spm.GetTempDownloadDir()
|
|
|
|
// fmt.Println("\nTemporary download directory:", downloadDir)
|
|
|
|
|
|
|
|
// // Download the APPINDEX
|
|
|
|
// appIndexPath := filepath.Join(downloadDir, "APPINDEX")
|
|
|
|
// spm.UpdateProgress(0, "Starting APPINDEX download")
|
|
|
|
// if err := spm.DownloadAppIndex(appIndexPath); err != nil {
|
|
|
|
// fmt.Println("\nError downloading APPINDEX:", err)
|
|
|
|
// done <- true
|
|
|
|
// os.Exit(1)
|
|
|
|
// }
|
|
|
|
|
|
|
|
// // Download the desired package version (e.g., nightly)
|
|
|
|
// packageName := "spitfire-browser"
|
|
|
|
// release := "nightly"
|
|
|
|
|
|
|
|
// spm.UpdateProgress(0, "Starting package download")
|
|
|
|
// if err := spm.DownloadPackageFromAppIndex(appIndexPath, packageName, release, downloadDir); err != nil {
|
|
|
|
// fmt.Println("\nError downloading package:", err)
|
|
|
|
// done <- true
|
|
|
|
// os.Exit(1)
|
|
|
|
// }
|
|
|
|
|
|
|
|
// // Decompress and install
|
|
|
|
// packagePath := filepath.Join(downloadDir, "browser-amd64-nightly-linux.tar.gz")
|
|
|
|
// spm.UpdateProgress(0, "Starting decompression")
|
|
|
|
// tempDir, err := spm.DecompressToTemp(packagePath)
|
|
|
|
// if err != nil {
|
|
|
|
// fmt.Println("\nError decompressing package:", err)
|
|
|
|
// done <- true
|
|
|
|
// os.Exit(1)
|
|
|
|
// }
|
|
|
|
// fmt.Println("\nDecompressed package to:", tempDir)
|
|
|
|
|
|
|
|
// installDir := "/home/czmisacz/Stažené/spitfire" // Change to your desired install directory
|
|
|
|
// spm.UpdateProgress(0, "Starting installation")
|
|
|
|
// if err := spm.MoveFilesToInstallDir(tempDir, installDir); err != nil {
|
|
|
|
// fmt.Println("\nError installing package:", err)
|
|
|
|
// done <- true
|
|
|
|
// os.Exit(1)
|
|
|
|
// }
|
|
|
|
|
|
|
|
// // Notify progress display to stop and finalize
|
|
|
|
// done <- true
|
|
|
|
// fmt.Printf("\nSuccessfully installed %s (%s) to %s\n", packageName, release, installDir)
|
|
|
|
// }
|