Installer/main.go
partisan ca57775f8f
All checks were successful
/ test-on-windows (push) Successful in 20s
/ test-on-alpine (push) Successful in 4s
added --silent and register values in windows registry keys
2025-02-16 22:57:24 +01:00

388 lines
10 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"flag"
"fmt"
"os"
"time"
"spitfire-installer/spm"
rl "github.com/gen2brain/raylib-go/raylib"
)
var (
transition = NewTransitionManager()
currentStep = 0
targetStep = 0
useDefault = false
step1DefaultRect rl.Rectangle
step1CustomRect rl.Rectangle
installer = NewInstaller()
// We no longer store circleToCenter or "display progress" here.
// Instead, we use the new CircleAnimator:
circleAnimator = NewCircleAnimator()
// textHoverFade => for the small circles info text in steps 0..2
textHoverFade float32
)
const finalStep = 3
func main() {
// Check for the --silent flag.
silent := flag.Bool("silent", false, "run installer without GUI")
flag.Parse()
if *silent {
runSilent()
return
}
monitor := rl.GetCurrentMonitor()
if monitor < 0 {
monitor = 0 // Fallback to the primary monitor
}
screenW := rl.GetMonitorWidth(monitor)
screenH := rl.GetMonitorHeight(monitor)
rl.InitWindow(int32(screenW), int32(screenH), "Spitfire Browser Installer")
rl.SetWindowState(rl.FlagFullscreenMode)
defer rl.CloseWindow()
refreshRate := rl.GetMonitorRefreshRate(monitor)
if refreshRate <= 0 {
refreshRate = 60
}
rl.SetTargetFPS(int32(refreshRate))
InitBackground(rl.GetScreenWidth(), rl.GetScreenHeight())
// Start the download+decompress in background immediately
installer.StartDownloadDecompress()
// Initially step 0 => circle is top-right => circleAnimator.PosAlpha=0
circleAnimator.SetStep(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
// Transition update
oldAlpha, oldScale, oldOffsetX, newAlpha, newScale, newOffsetX := transition.Update()
if !transition.IsActive() && currentStep != targetStep {
currentStep = targetStep
// Update circleAnimator step so it moves (0 => top-right, 3 => center)
circleAnimator.SetStep(currentStep)
}
// Poll SPM progress
installer.PollProgress()
// If error
if installer.LastError != nil {
fmt.Println("SPM Error:", installer.LastError)
}
// Update circle animator every frame
// This will ease the actual SPM progress => "DisplayProgress"
circleAnimator.Update(installer.Progress)
// textHoverFade for step<3
if currentStep < finalStep {
topRightX := float32(screenW - 80)
topRightY := float32(100)
radius := float32(30)
dx := mousePos.X - topRightX
dy := mousePos.Y - topRightY
dist := float32((dx*dx + dy*dy))
if dist < (radius+30)*(radius+30) {
textHoverFade += 0.1
if textHoverFade > 1 {
textHoverFade = 1
}
} else {
textHoverFade -= 0.1
if textHoverFade < 0 {
textHoverFade = 0
}
}
} else {
textHoverFade = 0
}
// Mouse input
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, 1, 0)
}
// Nav buttons if not final step
if !transition.IsActive() && currentStep < finalStep {
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)
}
}
// Draw the circle
drawInstallCircle(screenW, screenH, circleAnimator.PosAlpha, circleAnimator.DisplayProgress, installer.DoneInstall, textHoverFade)
rl.EndDrawing()
}
}
func runSilent() {
fmt.Println("Running installer in silent mode (default values will be used).")
// Start the download+decompress phase.
installer.StartDownloadDecompress()
// Poll until the download/decompression phase is done.
for !installer.DoneDownload {
time.Sleep(1 * time.Second)
}
// Start the final installation.
installer.FinalInstall()
// Poll until installation is finished.
for !installer.DoneInstall {
time.Sleep(1 * time.Second)
}
if installer.LastError != nil {
fmt.Printf("Installation failed: %v\n", installer.LastError)
os.Exit(1)
}
fmt.Println("Installation complete!")
// Optionally launch the app after install:
// spm.Run() or another function if needed.
os.Exit(0)
}
func handleInput(mousePos rl.Vector2, screenW, screenH, buttonW, buttonH int, prevX, prevY, nextX, nextY int32) {
switch currentStep {
case 0:
if overRect(mousePos, step1DefaultRect) {
useDefault = true
if !installer.IsInstalling && !installer.DoneInstall {
installer.FinalInstall()
}
startTransition(currentStep, finalStep)
}
if overRect(mousePos, step1CustomRect) {
useDefault = false
startTransition(currentStep, 1)
}
case 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)
case 2:
if overButton(mousePos, nextX, nextY, int32(buttonW), int32(buttonH)) {
if !installer.IsInstalling && !installer.DoneInstall {
installer.FinalInstall()
}
fmt.Printf("Installation started:\nDefault: %v\nColor: %s\nTheme: %s\nLayout: %s\n", useDefault, selectedColor, selectedTheme, selectedLayout)
startTransition(currentStep, finalStep)
}
if overButton(mousePos, int32(prevX), int32(prevY), int32(buttonW), int32(buttonH)) {
startTransition(currentStep, 1)
}
selectLayoutOption(mousePos)
}
}
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))
}
// Now we pass "displayProgress" (0..100) as a float, "posAlpha" in [0..1],
// plus "doneInstall" to show the "Run App" button.
func drawInstallCircle(screenW, screenH int, posAlpha, displayProgress float32, doneInstall bool, hoverAlpha float32) {
// Lerp position/radius
topRight := rl.Vector2{X: float32(screenW - 80), Y: 100}
center := rl.Vector2{X: float32(screenW) / 2, Y: float32(screenH)/2 - 50}
x := lerp(topRight.X, center.X, posAlpha)
y := lerp(topRight.Y, center.Y, posAlpha)
r := lerp(30, 60, posAlpha)
// Draw background circle
bgColor := rl.Color{255, 255, 255, 80}
fillColor := rl.Color{255, 255, 255, 200}
rl.DrawCircle(int32(x), int32(y), r, bgColor)
angle := displayProgress / 100.0 * 360.0
rl.DrawCircleSector(rl.Vector2{X: x, Y: y}, r, 0, angle, 60, fillColor)
// Draw numeric progress
txt := fmt.Sprintf("%3.0f%%", displayProgress)
var fontSize int32 = 18
if posAlpha > 0.5 {
fontSize = 24 // bigger in final steps
}
txtW := rl.MeasureText(txt, fontSize)
rl.DrawText(txt, int32(x)-txtW/2, int32(y)-(fontSize/2), fontSize, rl.White)
if currentStep < finalStep {
// show the task text with "hoverAlpha"
if hoverAlpha > 0 {
drawTextWrappedAlpha(installer.Task, int32(x)-100, int32(y)+35, 200, 18, rl.White, hoverAlpha)
}
} else {
// final step => always show text, lower so bigger circle won't overlap
drawTextWrapped(installer.Task, int32(x)-100, int32(y+r+10), 200, 18, rl.White)
if doneInstall {
drawRunAppButton(x, y+r+80)
}
}
}
// "Run App" button logic is the same.
func drawRunAppButton(cx, cy float32) {
w := float32(180)
h := float32(50)
rect := rl.Rectangle{X: cx - w/2, Y: cy, Width: w, Height: h}
hovered := overRect(rl.GetMousePosition(), rect)
drawRoundedRectButton(rect, "Start Spitfire", 1.0, hovered)
if hovered && rl.IsMouseButtonPressed(rl.MouseLeftButton) {
fmt.Println("Launching...")
spm.Run()
os.Exit(0)
}
}
// linear interpolation
func lerp(a, b, t float32) float32 {
return a + (b-a)*t
}
// Helper for text wrapping with alpha
func drawTextWrappedAlpha(text string, x, y, maxWidth, fontSize int32, color rl.Color, alpha float32) {
words := splitIntoWords(text)
line := ""
offsetY := int32(0)
for _, word := range words {
testLine := line + word + " "
if rl.MeasureText(testLine, fontSize) > int32(maxWidth) {
drawColoredText(line, x, y+offsetY, fontSize, color)
line = word + " "
offsetY += fontSize + 2
} else {
line = testLine
}
}
if line != "" {
drawColoredText(line, x, y+offsetY, fontSize, color)
}
}
// Same as drawTextWrapped, just using a color param
func drawColoredText(text string, x, y, fontSize int32, color rl.Color) {
rl.DrawText(text, x, y, fontSize, color)
}
// 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)
}