Fix font scaling issue
This commit is contained in:
@@ -28,7 +28,7 @@ const (
|
||||
volumeStepSize = 16
|
||||
openGLMajorVersion = 3
|
||||
openGLMinorVersion = 3
|
||||
apiURL = "http://localhost:5173/api/v1"
|
||||
apiURL = "https://portal.rhemawavetech.com/api/v1"
|
||||
)
|
||||
|
||||
type Application struct {
|
||||
|
||||
+95
-111
@@ -67,7 +67,6 @@ import (
|
||||
"rhemawavetech.com/rhemawave/internal/colors"
|
||||
"rhemawavetech.com/rhemawave/internal/db"
|
||||
"rhemawavetech.com/rhemawave/internal/fontManager"
|
||||
"rhemawavetech.com/rhemawave/internal/logger"
|
||||
"rhemawavetech.com/rhemawave/internal/mixer"
|
||||
colorPattern "rhemawavetech.com/rhemawave/internal/pattern/color"
|
||||
linePattern "rhemawavetech.com/rhemawave/internal/pattern/line"
|
||||
@@ -169,7 +168,6 @@ type ScrollerPage struct {
|
||||
wordIndex int
|
||||
lines *LineDeque
|
||||
lineHeight int32
|
||||
fontScale float32
|
||||
shader *shaderManager.Shader
|
||||
activePattern linePattern.LinePattern
|
||||
font *fontManager.FontEntry
|
||||
@@ -178,6 +176,7 @@ type ScrollerPage struct {
|
||||
colorPattern *colorPattern.ColorPattern
|
||||
scrollOffset float32
|
||||
scrollY float32
|
||||
fontScale float32
|
||||
|
||||
increment float32
|
||||
velocity float32
|
||||
@@ -195,78 +194,80 @@ type ScrollerPage struct {
|
||||
}
|
||||
|
||||
func (p *ScrollerPage) Init(ctx *PageCtx) error {
|
||||
// 1. Basic configuration and Deque allocation
|
||||
// We do this first so Destroy() has a valid object to clean up if we fail later.
|
||||
p.increment = 100.0
|
||||
p.velocity = 800.0
|
||||
p.SetAutoVelocity(true, -4000.0, 4000.0, 100.0, 2.5, 6.0)
|
||||
|
||||
standardFontSize := ctx.CfgPtr.StandardFontSize
|
||||
baseFontSize := ctx.CfgPtr.StandardFontSize
|
||||
p.fontScale = float32(standardFontSize) / float32(baseFontSize)
|
||||
|
||||
nLines := int(p.ScreenHeight/int32(standardFontSize)) + 5
|
||||
p.lines = NewLineDeque(nLines * 2)
|
||||
|
||||
// 2. Initialize Base and Managers
|
||||
p.Base.Init(ctx)
|
||||
sm := shaderManager.Get()
|
||||
fm := fontManager.Get()
|
||||
if fm == nil || sm == nil {
|
||||
return fmt.Errorf("critical systems (font/shader managers) not initialized")
|
||||
}
|
||||
|
||||
// 3. Resource Loading: Font
|
||||
// Get font
|
||||
font, ok := fm.GetFont("Scrambled")
|
||||
if !ok || font == nil {
|
||||
return fmt.Errorf("unable to fetch font 'Scrambled': font is nil")
|
||||
if !ok {
|
||||
return fmt.Errorf("unable to fetch font 'Scrambled'")
|
||||
}
|
||||
p.font = font
|
||||
|
||||
// 4. Resource Loading: Shader
|
||||
p.Base.Init(ctx)
|
||||
|
||||
// Get text shader
|
||||
textShader, ok := sm.Get("text")
|
||||
if !ok || textShader == nil {
|
||||
return fmt.Errorf("text shader not found or is nil")
|
||||
if !ok {
|
||||
return fmt.Errorf("text shader not found")
|
||||
}
|
||||
p.shader = textShader
|
||||
|
||||
// 5. Calculate Font Metrics (Now safe because p.font is guaranteed)
|
||||
spaceGlyph, ok := fm.GetGlyph(p.font.Name, ' ')
|
||||
if !ok {
|
||||
return fmt.Errorf("failed to get space glyph for font: %s", p.font.Name)
|
||||
}
|
||||
p.spaceWidth = int32(float32(spaceGlyph.Advance) * p.fontScale)
|
||||
|
||||
// Get line height (Using a temporary texture then immediate cleanup)
|
||||
tmpStrID, _, H, err := fm.NewCreateStringTexture(p.font.Name, "Mg", p.shader)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create sample texture for metrics: %w", err)
|
||||
}
|
||||
p.lineHeight = int32(float32(H) * p.fontScale)
|
||||
gl.DeleteTextures(1, &tmpStrID)
|
||||
|
||||
// 6. Load Data: Proclamations
|
||||
// Load proclamation words from database
|
||||
if err := p.loadProclamations(); err != nil {
|
||||
// If no proclamations, we must stop here or we'll crash in createLine
|
||||
p.Context.NavigateTo(PROCLAMATIONS)
|
||||
return fmt.Errorf("navigation triggered: %w", err)
|
||||
// Don't stop execution, just show fallback words
|
||||
p.words = strings.Fields("No Proclamations Selected!")
|
||||
}
|
||||
p.wordIndex = 0
|
||||
|
||||
// 7. Load Data: Patterns
|
||||
// Get line pattern from database
|
||||
var err error
|
||||
linePatternName := p.Base.Context.CfgPtr.LinePattern
|
||||
|
||||
p.activePattern, err = linePattern.Get(linePatternName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load line pattern '%s': %w", linePatternName, err)
|
||||
}
|
||||
|
||||
// Initialize color pattern
|
||||
var baseColors = []sdl.Color{
|
||||
colors.Tan, colors.PeachPuff, colors.LightCyan,
|
||||
colors.LightSeaGreen, colors.MediumTurquoise,
|
||||
colors.DarkCyan, colors.MediumPurple,
|
||||
colors.Tan,
|
||||
colors.PeachPuff,
|
||||
colors.LightCyan,
|
||||
colors.LightSeaGreen,
|
||||
colors.MediumTurquoise,
|
||||
colors.DarkCyan,
|
||||
colors.MediumPurple,
|
||||
}
|
||||
p.colorPattern = colorPattern.NewColorPattern(baseColors)
|
||||
|
||||
// Get font sizes from database (optimize: load once, cache as int32)
|
||||
// Match the working page's logic exactly:
|
||||
p.fontScale = float32(32) / float32(p.Base.Context.CfgPtr.StandardFontSize)
|
||||
|
||||
// Font setup
|
||||
nLines := int(float32(p.ScreenHeight) / 32.0) // 32 is your target visual size
|
||||
p.lines = NewLineDeque(nLines * 2)
|
||||
|
||||
// Calculate space width using the SCALE ratio, not the font size int
|
||||
spaceGlyph, ok := fm.GetGlyph(p.font.Name, ' ')
|
||||
if !ok {
|
||||
return fmt.Errorf("failed to get space glyph")
|
||||
}
|
||||
p.spaceWidth = int32(float32(spaceGlyph.Advance) * p.fontScale)
|
||||
|
||||
// Get line height using the SCALE ratio
|
||||
tmpStrID, _, H, err := fm.NewCreateStringTexture(p.font.Name, "Mg", p.shader)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create sample texture: %w", err)
|
||||
}
|
||||
p.lineHeight = int32(float32(H) * p.fontScale)
|
||||
gl.DeleteTextures(1, &tmpStrID)
|
||||
|
||||
p.lineCtx = &linePattern.LineRenderContext{
|
||||
ScreenWidth: p.ScreenWidth,
|
||||
Font: p.font,
|
||||
@@ -274,23 +275,37 @@ func (p *ScrollerPage) Init(ctx *PageCtx) error {
|
||||
SpaceWidth: p.spaceWidth,
|
||||
}
|
||||
|
||||
// 8. Generate Initial Visual State
|
||||
// Create initial lines
|
||||
for i := 0; i < nLines; i++ {
|
||||
if err := p.createLine(i, true); err != nil {
|
||||
return fmt.Errorf("failed to create initial line %d: %w", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
// 9. Audio Initialization
|
||||
// Audio setup
|
||||
audioDeviceName := p.Base.Context.CfgPtr.SelectedAudioDevice
|
||||
|
||||
// Switch audio devices if needed
|
||||
if mixer.CurrentDevice() != audioDeviceName {
|
||||
_ = mixer.SwitchDevice(audioDeviceName)
|
||||
if err := mixer.SwitchDevice(audioDeviceName); err != nil {
|
||||
} else {
|
||||
}
|
||||
}
|
||||
|
||||
// Start audio playback
|
||||
selectedAudio := p.Base.Context.CfgPtr.SelectedAudio
|
||||
|
||||
assetsDir := p.Base.Context.CfgPtr.AssetsDir
|
||||
if selectedAudio != "" {
|
||||
_ = mixer.Play(filepath.Join(assetsDir, selectedAudio), true, p.Base.Context.CfgPtr)
|
||||
|
||||
if selectedAudio == "" {
|
||||
} else {
|
||||
if err := mixer.Play(filepath.Join(assetsDir, selectedAudio), true, p.Base.Context.CfgPtr); err != nil {
|
||||
}
|
||||
}
|
||||
|
||||
currentDevice := mixer.CurrentDevice()
|
||||
if currentDevice == "" {
|
||||
currentDevice = "default"
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -300,26 +315,22 @@ func (p *ScrollerPage) Init(ctx *PageCtx) error {
|
||||
func (p *ScrollerPage) loadProclamations() error {
|
||||
p.words = []string{}
|
||||
|
||||
// 1. Query only Selected AND Available records directly from the DB
|
||||
// Get all selected proclamations from database
|
||||
var proclamations []db.Proclamation
|
||||
// TODO: Uncomment and replace this line once available is added
|
||||
// err := p.Base.Context.DbPtr.Where("selected = ? AND available = ?", true, true).Find(&proclamations).Error
|
||||
|
||||
err := p.Base.Context.DbPtr.Where("selected = ?", true).Find(&proclamations).Error
|
||||
|
||||
err := p.Context.DbPtr.Where("selected = ?", true).Find(&proclamations).Error
|
||||
if err != nil {
|
||||
logger.Error("Database query failed: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. Build word list
|
||||
// Build word list from selected, available proclamations
|
||||
for _, proclamation := range proclamations {
|
||||
// strings.Fields handles multiple spaces and newlines automatically
|
||||
words := strings.Fields(proclamation.Content)
|
||||
p.words = append(p.words, words...)
|
||||
if proclamation.Selected && proclamation.Available {
|
||||
// Split content into words and add to list
|
||||
words := strings.Fields(proclamation.Content)
|
||||
p.words = append(p.words, words...)
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Handle empty state
|
||||
if len(p.words) == 0 {
|
||||
p.words = strings.Fields("No Proclamations Selected!")
|
||||
return fmt.Errorf("no proclamations selected or available")
|
||||
@@ -343,8 +354,8 @@ func (p *ScrollerPage) createLine(index int, isInitial bool) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create string item for word '%s': %w", word, err)
|
||||
}
|
||||
item.W = int32(float32(item.W) * p.fontScale)
|
||||
item.H = int32(float32(item.H) * p.fontScale)
|
||||
|
||||
item.Scale(p.fontScale)
|
||||
|
||||
if len(line.words) > 0 {
|
||||
line.width += p.spaceWidth
|
||||
@@ -477,8 +488,8 @@ func (p *ScrollerPage) addLineAtBottom() error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create string item for word '%s': %w", word, err)
|
||||
}
|
||||
item.W = int32(float32(item.W) * p.fontScale)
|
||||
item.H = int32(float32(item.H) * p.fontScale)
|
||||
|
||||
item.Scale(p.fontScale)
|
||||
|
||||
if len(line.words) > 0 {
|
||||
line.width += p.spaceWidth
|
||||
@@ -523,8 +534,8 @@ func (p *ScrollerPage) addLineAtTop() error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create string item for word '%s': %w", word, err)
|
||||
}
|
||||
item.W = int32(float32(item.W) * p.fontScale)
|
||||
item.H = int32(float32(item.H) * p.fontScale)
|
||||
|
||||
item.Scale(p.fontScale)
|
||||
|
||||
if len(line.words) > 0 {
|
||||
line.width += p.spaceWidth
|
||||
@@ -571,61 +582,34 @@ func (p *ScrollerPage) Render() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Drop references
|
||||
func (p *ScrollerPage) Destroy() error {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 1. Audio Guard
|
||||
// Ensure Context and CfgPtr exist before stopping the mixer
|
||||
if p.Context != nil && p.Context.CfgPtr != nil {
|
||||
mixer.Stop(1000, p.Context.CfgPtr)
|
||||
}
|
||||
mixer.Stop(1000, p.Base.Context.CfgPtr)
|
||||
|
||||
// 2. Lines / OpenGL Guard
|
||||
// We only iterate and delete textures if the deque was actually allocated
|
||||
if p.lines != nil {
|
||||
for i := 0; i < p.lines.Len(); i++ {
|
||||
line := p.lines.Get(i)
|
||||
for j := range line.words {
|
||||
// Double-check word ID to avoid redundant GL calls
|
||||
if line.words[j].ID != 0 {
|
||||
gl.DeleteTextures(1, &line.words[j].ID)
|
||||
line.words[j].ID = 0
|
||||
}
|
||||
// GL cleanup — delete all text textures
|
||||
for i := 0; i < p.lines.Len(); i++ {
|
||||
line := p.lines.Get(i)
|
||||
for j := range line.words {
|
||||
if id := line.words[j].ID; id != 0 {
|
||||
gl.DeleteTextures(1, &line.words[j].ID)
|
||||
line.words[j].ID = 0
|
||||
}
|
||||
}
|
||||
p.lines.Clear() // Optional: clear circular buffer internal refs
|
||||
p.lines = nil
|
||||
}
|
||||
|
||||
// 3. Pointer Cleanup Guards
|
||||
// Explicitly setting to nil to help the Garbage Collector,
|
||||
// but only if they aren't already nil.
|
||||
if p.words != nil {
|
||||
p.words = nil
|
||||
}
|
||||
|
||||
if p.lineCtx != nil {
|
||||
p.lineCtx = nil
|
||||
}
|
||||
|
||||
if p.font != nil {
|
||||
p.font = nil
|
||||
}
|
||||
|
||||
if p.shader != nil {
|
||||
p.shader = nil
|
||||
}
|
||||
|
||||
if p.colorPattern != nil {
|
||||
p.colorPattern = nil
|
||||
}
|
||||
|
||||
// Final GPU sync
|
||||
gl.Flush()
|
||||
|
||||
// Drop references for GC
|
||||
p.lines = nil
|
||||
p.words = nil
|
||||
p.lineCtx = nil
|
||||
p.font = nil
|
||||
p.shader = nil
|
||||
p.colorPattern = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user