Files
radiantwave-legacy/internal/page/logViewerPage.go
T

252 lines
6.6 KiB
Go

package page
import (
"fmt"
"strconv"
"strings"
"github.com/go-gl/gl/v3.3-core/gl"
"github.com/go-gl/mathgl/mgl32"
"github.com/veandco/go-sdl2/sdl"
"rhemawavetech.com/rhemawave/internal/db"
"rhemawavetech.com/rhemawave/internal/fontManager"
"rhemawavetech.com/rhemawave/internal/shaderManager"
)
type LogViewerPage struct {
Base
titleTextureID uint32
titleTextureWidth int32
titleTextureHeight int32
lines []string
lineTextureIDs []uint32
lineTextureWidths []int32
lineTextureHeights []int32
hintTextureID uint32
hintTextureWidth int32
hintTextureHeight int32
scrollOffset int32
maxScrollOffset int32
lineHeight int32
visibleHeight int32
}
func (p *LogViewerPage) Init(app ApplicationInterface) error {
if err := p.Base.Init(app); err != nil {
return fmt.Errorf("failed base page initialization: %w", err)
}
logs, err := db.GetLogs(1000)
if err != nil {
return fmt.Errorf("failed to get logs: %w", err)
}
// Format and word-wrap each log entry
for _, entry := range logs {
formatted := fmt.Sprintf("[%s] [%s] %s", entry.Timestamp, entry.Level, entry.Message)
wrapped := wrapText(formatted, 100)
p.lines = append(p.lines, strings.Split(wrapped, "\n")...)
}
applicationFont, err := db.GetConfigValue("application_font")
if err != nil {
return fmt.Errorf("retrieving application_font from db: %w", err)
}
fm := fontManager.Get()
textShader, ok := shaderManager.Get().Get("text")
if !ok {
return fmt.Errorf("unable to fetch text shader")
}
// Title texture
titleID, titleW, titleH, err := fm.CreateStringTexture(applicationFont, "Application Logs", textShader)
if err != nil {
return fmt.Errorf("failed to create title texture: %w", err)
}
p.titleTextureID = titleID
p.titleTextureWidth = titleW
p.titleTextureHeight = titleH
// Line textures
p.lineTextureIDs = make([]uint32, len(p.lines))
p.lineTextureWidths = make([]int32, len(p.lines))
p.lineTextureHeights = make([]int32, len(p.lines))
for i, line := range p.lines {
if strings.TrimSpace(line) == "" {
continue
}
texID, w, h, err := fm.CreateStringTexture(applicationFont, line, textShader)
if err != nil {
return fmt.Errorf("failed to create line texture: %w", err)
}
p.lineTextureIDs[i] = texID
p.lineTextureWidths[i] = w
p.lineTextureHeights[i] = h
}
// Hint texture
hintText := "Arrow Keys / PgUp/PgDn to scroll | F1|2|3 to Exit"
hintID, hintW, hintH, err := fm.CreateStringTexture(applicationFont, hintText, textShader)
if err != nil {
return fmt.Errorf("failed to create hint texture: %w", err)
}
p.hintTextureID = hintID
p.hintTextureWidth = hintW
p.hintTextureHeight = hintH
return nil
}
func (p *LogViewerPage) HandleEvent(event *sdl.Event) error {
switch e := (*event).(type) {
case *sdl.KeyboardEvent:
if e.Type == sdl.KEYDOWN {
switch e.Keysym.Sym {
case sdl.K_BACKSPACE:
p.App.SchedulePop()
case sdl.K_UP:
p.scrollOffset -= p.lineHeight
if p.scrollOffset < 0 {
p.scrollOffset = 0
}
case sdl.K_DOWN:
p.scrollOffset += p.lineHeight
if p.scrollOffset > p.maxScrollOffset {
p.scrollOffset = p.maxScrollOffset
}
case sdl.K_PAGEUP:
p.scrollOffset -= p.visibleHeight
if p.scrollOffset < 0 {
p.scrollOffset = 0
}
case sdl.K_PAGEDOWN:
p.scrollOffset += p.visibleHeight
if p.scrollOffset > p.maxScrollOffset {
p.scrollOffset = p.maxScrollOffset
}
case sdl.K_HOME:
p.scrollOffset = 0
case sdl.K_END:
p.scrollOffset = p.maxScrollOffset
}
}
case *sdl.MouseWheelEvent:
p.scrollOffset -= e.Y * p.lineHeight * 3
if p.scrollOffset < 0 {
p.scrollOffset = 0
}
if p.scrollOffset > p.maxScrollOffset {
p.scrollOffset = p.maxScrollOffset
}
}
return nil
}
func (p *LogViewerPage) Update(dt float32) error {
return nil
}
func (p *LogViewerPage) Render() error {
baseFontSizeStr, err := db.GetConfigValue("init_font_size")
if err != nil {
return fmt.Errorf("retrieving init_font_size from db: %w", err)
}
baseFontSize, err := strconv.Atoi(baseFontSizeStr)
if err != nil {
return fmt.Errorf("parsing init_font_size: %w", err)
}
sm := shaderManager.Get()
textShader, _ := sm.Get("text")
white := sdl.Color{R: 255, G: 255, B: 255, A: 255}
grey := sdl.Color{R: 180, G: 180, B: 180, A: 255}
titleFontSize := int32(48)
titleScale := float32(titleFontSize) / float32(baseFontSize)
contentFontSize := int32(24)
contentScale := float32(contentFontSize) / float32(baseFontSize)
hintFontSize := int32(20)
hintScale := float32(hintFontSize) / float32(baseFontSize)
margin := int32(40)
lineSpacing := int32(4)
p.lineHeight = contentFontSize + lineSpacing
contentColumnWidth := int32(float32(p.Base.ScreenWidth) * 0.55)
contentLeftX := p.Base.ScreenCenterX - contentColumnWidth/2
// Title
titleH := float32(p.titleTextureHeight) * titleScale
titleW := float32(p.titleTextureWidth) * titleScale
titleX := p.Base.ScreenCenterX - int32(titleW/2)
titleY := p.Base.ScreenHeight - margin - int32(titleH)
p.Base.RenderTexture(textShader, p.titleTextureID, mgl32.Vec2{float32(titleX), float32(titleY)}, mgl32.Vec2{titleW, titleH}, white)
// Hint at bottom
hintH := float32(p.hintTextureHeight) * hintScale
hintW := float32(p.hintTextureWidth) * hintScale
hintX := p.Base.ScreenCenterX - int32(hintW/2)
hintY := margin
p.Base.RenderTexture(textShader, p.hintTextureID, mgl32.Vec2{float32(hintX), float32(hintY)}, mgl32.Vec2{hintW, hintH}, grey)
// Content area bounds
contentTop := titleY - margin
contentBottom := hintY + int32(hintH) + margin
p.visibleHeight = contentTop - contentBottom
// Total content height
totalContentHeight := int32(len(p.lines)) * p.lineHeight
p.maxScrollOffset = totalContentHeight - p.visibleHeight
if p.maxScrollOffset < 0 {
p.maxScrollOffset = 0
}
// Render visible lines
currentY := contentTop - p.lineHeight + p.scrollOffset
for i, line := range p.lines {
if strings.TrimSpace(line) == "" {
currentY -= p.lineHeight
continue
}
if p.lineTextureIDs[i] == 0 {
currentY -= p.lineHeight
continue
}
if currentY > contentTop || currentY+p.lineHeight < contentBottom {
currentY -= p.lineHeight
continue
}
lineW := float32(p.lineTextureWidths[i]) * contentScale
lineH := float32(p.lineTextureHeights[i]) * contentScale
p.Base.RenderTexture(textShader, p.lineTextureIDs[i], mgl32.Vec2{float32(contentLeftX), float32(currentY)}, mgl32.Vec2{lineW, lineH}, white)
currentY -= p.lineHeight
}
return nil
}
func (p *LogViewerPage) Destroy() error {
gl.DeleteTextures(1, &p.titleTextureID)
gl.DeleteTextures(1, &p.hintTextureID)
for _, texID := range p.lineTextureIDs {
if texID != 0 {
gl.DeleteTextures(1, &texID)
}
}
return p.Base.Destroy()
}