package page import ( "fmt" "github.com/go-gl/gl/v3.3-core/gl" "github.com/veandco/go-sdl2/sdl" "rhemawavetech.com/rhemawave/internal/colors" "rhemawavetech.com/rhemawave/internal/db" "rhemawavetech.com/rhemawave/internal/fontManager" "rhemawavetech.com/rhemawave/internal/shaderManager" ) type Settings struct { Base font *fontManager.FontEntry textShader *shaderManager.Shader solidShader *shaderManager.Shader title StringItem prompt StringItem options []Option verticalMargin int32 bgHighlight PageItem highlightIndex int } type Option struct { target pageType label StringItem } func (p *Settings) Init(ctx *PageCtx) error { var err error p.verticalMargin = int32(20) // Get font fm := fontManager.Get() font, ok := fm.GetFont("SpaceMonoNerdFont-Regular") if !ok { return fmt.Errorf("failed to get font: %w", sdl.GetError()) } p.font = font // Get shaders sm := shaderManager.Get() p.textShader, ok = sm.Get("text") if !ok { return fmt.Errorf("failed to get text shader: %w", sdl.GetError()) } p.solidShader, ok = sm.Get("solid_color") if !ok { return fmt.Errorf("failed to get solid_color shader: %w", sdl.GetError()) } // Initialize base if err := p.Base.Init(ctx); err != nil { return fmt.Errorf("failed base page initialization: %w", err) } // Create title p.title, err = NewStringItem("Settings", font, colors.White) if err != nil { return fmt.Errorf("creating title: %w", err) } // Create prompt p.prompt, err = NewStringItem("Use Arrow Keys to Navigate, Enter to Select, or F1 to return to the Main Menu", font, colors.White) if err != nil { return fmt.Errorf("creating prompt: %w", err) } // Create options proclamationsLabel, err := NewStringItem("Proclamations", font, colors.White) if err != nil { return fmt.Errorf("creating proclamations label: %w", err) } p.options = append(p.options, Option{ target: PROCLAMATIONS, label: proclamationsLabel, }) audioLabel, err := NewStringItem("Audio", font, colors.White) if err != nil { return fmt.Errorf("creating audio label: %w", err) } p.options = append(p.options, Option{ target: AUDIO, label: audioLabel, }) emailLabel, err := NewStringItem("Email Address", font, colors.White) if err != nil { return fmt.Errorf("creating email label: %w", err) } p.options = append(p.options, Option{ target: EMAIL_ADDRESS, label: emailLabel, }) licenseKeyLabel, err := NewStringItem("License Key", font, colors.White) if err != nil { return fmt.Errorf("creating license key label: %w", err) } p.options = append(p.options, Option{ target: LICENSE_KEY, label: licenseKeyLabel, }) wifiLabel, err := NewStringItem("WiFi Setup", font, colors.White) if err != nil { return fmt.Errorf("creating wifi label: %w", err) } p.options = append(p.options, Option{ target: WIFI, label: wifiLabel, }) logsLabel, err := NewStringItem("Logs", font, colors.White) if err != nil { return fmt.Errorf("creating logs label: %w", err) } p.options = append(p.options, Option{ target: LOGS, label: logsLabel, }) // TODO: Add entry for "Tutorial" // TODO: Add entry for "About" // Get base font size from database config, err := db.GetConfig(p.Context.DbPtr) if err != nil { panic(err) } baseFontSize := config.StandardFontSize // Calculate scales titleScale := float32(64) / float32(baseFontSize) scale := float32(32) / float32(baseFontSize) // Apply scales p.title.Scale(titleScale) p.prompt.Scale(scale) for i := range p.options { p.options[i].label.Scale(scale) } // Create background highlight using first option's dimensions p.bgHighlight = PageItem{ ID: 0, W: p.options[0].label.W, H: p.options[0].label.H, Color: colors.White, } p.highlightIndex = 0 // Position title at bottom p.title.X = p.Base.ScreenCenterX - p.title.W/2 p.title.Y = p.ScreenHeight - p.title.H - p.verticalMargin*2 // Position prompt above title p.prompt.X = p.ScreenCenterX - int32(float32(p.prompt.W)/2) p.prompt.Y = p.title.Y - p.verticalMargin*2 - p.prompt.H // If this value starts at 0, nothing is further left than that... var furthestLeft int32 = p.ScreenWidth // < more to the left, > more to the right, = same // Position options stacked upward from prompt for i, option := range p.options { p.options[i].label.X = p.ScreenCenterX - option.label.W/2 // Calculate horizontal center posision if p.options[i].label.X < furthestLeft { // If the left most pixel is further left than furthestLeft, indicating a longer line: // Then update furthestLeft furthestLeft = p.options[i].label.X } if i == 0 { p.options[i].label.Y = int32(float32(p.prompt.Y)-(float32(p.verticalMargin)*4)) - option.label.H } else { p.options[i].label.Y = p.options[i-1].label.Y - option.label.H - 10 } } // Now, re-iterate over the options and justify everything to furthestLeft for i := range p.options { p.options[i].label.X = furthestLeft } return nil } func (p *Settings) Update(dt float32) error { return nil } func (p *Settings) HandleEvent(event *sdl.Event) error { switch e := (*event).(type) { case *sdl.KeyboardEvent: if e.Type != sdl.KEYDOWN { return nil } switch e.Keysym.Sym { case sdl.K_UP: if p.highlightIndex > 0 { p.highlightIndex-- } case sdl.K_DOWN: if p.highlightIndex < len(p.options)-1 { p.highlightIndex++ } case sdl.K_RETURN: if p.highlightIndex >= 0 && p.highlightIndex < len(p.options) { selectedOption := p.options[p.highlightIndex] p.Context.NavigateTo(selectedOption.target) } case sdl.K_BACKSPACE: p.Context.NavigateTo(HOME) } } return nil } func (p *Settings) Render() error { // Render title and prompt p.RenderTexture(p.textShader, p.title.ID, p.title.Position(), p.title.RawDimensions(), p.title.Color) p.RenderTexture(p.textShader, p.prompt.ID, p.prompt.Position(), p.prompt.RawDimensions(), p.prompt.Color) // Render highlight behind selected option p.RenderSolidColorQuad( p.solidShader, p.options[p.highlightIndex].label.Position(), p.options[p.highlightIndex].label.RawDimensions(), p.bgHighlight.Color, ) // Render all options with appropriate colors for i, option := range p.options { if p.highlightIndex == i { option.label.Color = colors.Black } else { option.label.Color = colors.White } p.RenderTexture(p.textShader, option.label.ID, option.label.Position(), option.label.RawDimensions(), option.label.Color) } return nil } func (p *Settings) Destroy() error { gl.DeleteTextures(1, &p.title.ID) gl.DeleteTextures(1, &p.prompt.ID) for i := range p.options { gl.DeleteTextures(1, &p.options[i].label.ID) } return nil }