Fix author logo preview, scrolling, and icons
This commit is contained in:
parent
a15b2668d3
commit
91c6caeaa0
|
|
@ -1223,11 +1223,12 @@ func buildAuthorMenuTab(state *appState) fyne.CanvasObject {
|
|||
info := widget.NewLabel("DVD menus are generated using the VideoTools theme and IBM Plex Mono. Menu settings apply only to disc authoring.")
|
||||
info.Wrapping = fyne.TextWrapWord
|
||||
|
||||
previewBox := buildMenuBox("Logo Preview", container.NewVBox(
|
||||
logoPreviewGroup := container.NewVBox(
|
||||
widget.NewLabel("Logo Preview:"),
|
||||
logoPreviewLabel,
|
||||
logoPreview,
|
||||
logoPreviewSize,
|
||||
))
|
||||
)
|
||||
|
||||
menuCore := buildMenuBox("Menu Core", container.NewVBox(
|
||||
createMenuCheck,
|
||||
|
|
@ -1247,6 +1248,7 @@ func buildAuthorMenuTab(state *appState) fyne.CanvasObject {
|
|||
widget.NewLabel("Logo Path:"),
|
||||
logoLabel,
|
||||
logoPickButton,
|
||||
logoPreviewGroup,
|
||||
widget.NewLabel("Logo Position:"),
|
||||
logoPositionSelect,
|
||||
widget.NewLabel("Logo Scale:"),
|
||||
|
|
@ -1270,8 +1272,6 @@ func buildAuthorMenuTab(state *appState) fyne.CanvasObject {
|
|||
sectionGap(),
|
||||
navigation,
|
||||
sectionGap(),
|
||||
previewBox,
|
||||
sectionGap(),
|
||||
info,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ var (
|
|||
logger = log.New(os.Stderr, "[videotools] ", log.LstdFlags|log.Lmicroseconds)
|
||||
filePath string
|
||||
historyMax = 500
|
||||
debugOn = false
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -73,6 +74,14 @@ func getStackTrace() string {
|
|||
return string(buf[:n])
|
||||
}
|
||||
|
||||
// RecoverPanic logs a recovered panic with a stack trace.
|
||||
// Intended for use in deferred calls inside goroutines.
|
||||
func RecoverPanic() {
|
||||
if r := recover(); r != nil {
|
||||
Crash(CatSystem, "Recovered panic: %v", r)
|
||||
}
|
||||
}
|
||||
|
||||
// Error logs an error message with a category (always logged, even when debug is off)
|
||||
func Error(cat Category, format string, args ...interface{}) {
|
||||
msg := fmt.Sprintf("%s ERROR: %s", cat, fmt.Sprintf(format, args...))
|
||||
|
|
@ -89,6 +98,9 @@ func Error(cat Category, format string, args ...interface{}) {
|
|||
|
||||
// Debug logs a debug message with a category
|
||||
func Debug(cat Category, format string, args ...interface{}) {
|
||||
if !debugOn {
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf("%s %s", cat, fmt.Sprintf(format, args...))
|
||||
timestamp := time.Now().Format(time.RFC3339Nano)
|
||||
if file != nil {
|
||||
|
|
@ -144,3 +156,13 @@ func Close() {
|
|||
file.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// SetDebug enables or disables debug logging.
|
||||
func SetDebug(enabled bool) {
|
||||
debugOn = enabled
|
||||
}
|
||||
|
||||
// FilePath returns the active log file path, if initialized.
|
||||
func FilePath() string {
|
||||
return filePath
|
||||
}
|
||||
|
|
|
|||
|
|
@ -122,7 +122,6 @@ func (p *UnifiedPlayer) Load(path string, offset time.Duration) error {
|
|||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logging.Crash(logging.CatPlayer, "Panic in Load(): %v", r)
|
||||
return fmt.Errorf("panic during video loading: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
|
|
@ -159,8 +158,7 @@ func (p *UnifiedPlayer) Load(path string, offset time.Duration) error {
|
|||
// Initialize audio context for playback
|
||||
sampleRate := 48000
|
||||
channels := 2
|
||||
bytesPerSample := 2 // 16-bit = 2 bytes
|
||||
|
||||
|
||||
ctx, ready, err := oto.NewContext(&oto.NewContextOptions{
|
||||
SampleRate: sampleRate,
|
||||
ChannelCount: channels,
|
||||
|
|
@ -189,7 +187,6 @@ func (p *UnifiedPlayer) Load(path string, offset time.Duration) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// SeekToTime seeks to a specific time without restarting processes
|
||||
func (p *UnifiedPlayer) SeekToTime(offset time.Duration) error {
|
||||
|
|
@ -639,72 +636,21 @@ func (p *UnifiedPlayer) readAudioStream() {
|
|||
}
|
||||
}()
|
||||
|
||||
buffer := make([]byte, 4096) // 85ms chunks
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-p.ctx.Done():
|
||||
logging.Debug(logging.CatPlayer, "Audio reading goroutine stopped")
|
||||
return
|
||||
|
||||
default:
|
||||
// Read from audio pipe
|
||||
n, err := p.audioPipeReader.Read(buffer)
|
||||
if err != nil && err.Error() != "EOF" {
|
||||
logging.Error(logging.CatPlayer, "Audio read error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Initialize audio player if needed
|
||||
if p.audioPlayer == nil && p.audioContext != nil {
|
||||
player, err := p.audioContext.NewPlayer(p.audioPipeReader)
|
||||
if err != nil {
|
||||
logging.Error(logging.CatPlayer, "Failed to create audio player: %v", err)
|
||||
return
|
||||
}
|
||||
p.audioPlayer = player
|
||||
logging.Info(logging.CatPlayer, "Audio player created successfully")
|
||||
}
|
||||
|
||||
// Write audio data to player buffer
|
||||
if p.audioPlayer != nil {
|
||||
p.audioPlayer.Write(buffer[:n])
|
||||
}
|
||||
|
||||
// Buffer for sync monitoring (keep small to avoid memory issues)
|
||||
if len(p.audioBuffer) > 32768 { // Max 1 second at 48kHz
|
||||
p.audioBuffer = p.audioBuffer[len(p.audioBuffer)-16384:] // Keep half
|
||||
}
|
||||
|
||||
// Simple audio sync timing
|
||||
p.updateAVSync()
|
||||
}
|
||||
if p.audioContext == nil {
|
||||
logging.Error(logging.CatPlayer, "Audio context is not initialized")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
p.mu.Lock()
|
||||
if p.audioPlayer == nil {
|
||||
p.audioPlayer = p.audioContext.NewPlayer(p.audioPipeReader)
|
||||
p.audioPlayer.Play()
|
||||
logging.Info(logging.CatPlayer, "Audio player created successfully")
|
||||
}
|
||||
p.mu.Unlock()
|
||||
|
||||
ticker := time.NewTicker(100 * time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-p.ctx.Done():
|
||||
logging.Debug(logging.CatPlayer, "Audio reading goroutine stopped")
|
||||
return
|
||||
case <-ticker.C:
|
||||
p.updateAVSync()
|
||||
}
|
||||
}
|
||||
<-p.ctx.Done()
|
||||
logging.Debug(logging.CatPlayer, "Audio reading goroutine stopped")
|
||||
}
|
||||
|
||||
// readVideoStream reads video frames from the video pipe
|
||||
|
|
|
|||
|
|
@ -562,14 +562,8 @@ func (f *FastVScroll) CreateRenderer() fyne.WidgetRenderer {
|
|||
}
|
||||
|
||||
func (f *FastVScroll) Scrolled(ev *fyne.ScrollEvent) {
|
||||
// Multiply scroll speed by 12x for much faster navigation
|
||||
fastEvent := &fyne.ScrollEvent{
|
||||
Scrolled: fyne.Delta{
|
||||
DX: ev.Scrolled.DX * 12.0,
|
||||
DY: ev.Scrolled.DY * 12.0,
|
||||
},
|
||||
}
|
||||
f.scroll.Scrolled(fastEvent)
|
||||
// Increase scroll speed moderately without overshooting content bounds.
|
||||
f.ScrollBy(ev.Scrolled.DY * 4.0)
|
||||
}
|
||||
|
||||
// ScrollBy scrolls the content by a delta in pixels (positive = down).
|
||||
|
|
@ -577,7 +571,11 @@ func (f *FastVScroll) ScrollBy(delta float32) {
|
|||
if f == nil || f.scroll == nil || f.scroll.Content == nil {
|
||||
return
|
||||
}
|
||||
max := f.scroll.Content.MinSize().Height - f.scroll.Size().Height
|
||||
content := f.scroll.Content
|
||||
max := content.Size().Height - f.scroll.Size().Height
|
||||
if max <= 0 {
|
||||
max = content.MinSize().Height - f.scroll.Size().Height
|
||||
}
|
||||
if max < 0 {
|
||||
max = 0
|
||||
}
|
||||
|
|
@ -655,7 +653,10 @@ func (d *DraggableVScroll) CreateRenderer() fyne.WidgetRenderer {
|
|||
func (d *DraggableVScroll) Dragged(ev *fyne.DragEvent) {
|
||||
// Calculate the scroll position based on drag position
|
||||
size := d.scroll.Size()
|
||||
contentSize := d.content.MinSize()
|
||||
contentSize := d.content.Size()
|
||||
if contentSize.Height == 0 {
|
||||
contentSize = d.content.MinSize()
|
||||
}
|
||||
|
||||
if contentSize.Height <= size.Height {
|
||||
return // No scrolling needed
|
||||
|
|
@ -688,7 +689,10 @@ func (d *DraggableVScroll) DragEnd() {
|
|||
func (d *DraggableVScroll) Tapped(ev *fyne.PointEvent) {
|
||||
// Jump to tapped position
|
||||
size := d.scroll.Size()
|
||||
contentSize := d.content.MinSize()
|
||||
contentSize := d.content.Size()
|
||||
if contentSize.Height == 0 {
|
||||
contentSize = d.content.MinSize()
|
||||
}
|
||||
|
||||
if contentSize.Height <= size.Height {
|
||||
return
|
||||
|
|
@ -711,14 +715,22 @@ func (d *DraggableVScroll) Tapped(ev *fyne.PointEvent) {
|
|||
|
||||
// Scrolled handles scroll events (mouse wheel)
|
||||
func (d *DraggableVScroll) Scrolled(ev *fyne.ScrollEvent) {
|
||||
// Multiply scroll speed by 2.5x for faster scrolling
|
||||
fastEvent := &fyne.ScrollEvent{
|
||||
Scrolled: fyne.Delta{
|
||||
DX: ev.Scrolled.DX * 2.5,
|
||||
DY: ev.Scrolled.DY * 2.5,
|
||||
},
|
||||
// Increase scroll speed modestly while clamping to content bounds.
|
||||
contentSize := d.content.Size()
|
||||
if contentSize.Height == 0 {
|
||||
contentSize = d.content.MinSize()
|
||||
}
|
||||
d.scroll.Scrolled(fastEvent)
|
||||
max := contentSize.Height - d.scroll.Size().Height
|
||||
if max < 0 {
|
||||
max = 0
|
||||
}
|
||||
newY := d.scroll.Offset.Y + (ev.Scrolled.DY * 2.0)
|
||||
if newY < 0 {
|
||||
newY = 0
|
||||
} else if newY > max {
|
||||
newY = max
|
||||
}
|
||||
d.scroll.ScrollToOffset(fyne.NewPos(d.scroll.Offset.X, newY))
|
||||
}
|
||||
|
||||
type draggableScrollRenderer struct {
|
||||
|
|
|
|||
|
|
@ -285,9 +285,9 @@ func MakeIconButton(symbol, tooltip string, tapped func()) *widget.Button {
|
|||
func LoadAppIcon() fyne.Resource {
|
||||
var iconFiles []string
|
||||
if runtime.GOOS == "windows" {
|
||||
iconFiles = []string{"VT_Icon.ico", "VT_Icon.png", "VT_Icon.svg"}
|
||||
iconFiles = []string{"VT_Icon.ico"}
|
||||
} else {
|
||||
iconFiles = []string{"VT_Icon.png", "VT_Icon.svg", "VT_Icon.ico"}
|
||||
iconFiles = []string{"VT_Icon.png"}
|
||||
}
|
||||
var search []string
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user