Compare commits

...

4 Commits

2 changed files with 120 additions and 10 deletions

View File

@ -2,6 +2,7 @@ package ui
import (
"fmt"
"image"
"image/color"
"strings"
"time"
@ -15,6 +16,110 @@ import (
"git.leaktechnologies.dev/stu/VideoTools/internal/utils"
)
// stripedProgress renders a progress bar with a tinted stripe pattern.
type stripedProgress struct {
widget.BaseWidget
progress float64
color color.Color
bg color.Color
offset float64
}
func newStripedProgress(col color.Color) *stripedProgress {
sp := &stripedProgress{
progress: 0,
color: col,
bg: color.RGBA{R: 34, G: 38, B: 48, A: 255}, // dark neutral
}
sp.ExtendBaseWidget(sp)
return sp
}
func (s *stripedProgress) SetProgress(p float64) {
if p < 0 {
p = 0
}
if p > 1 {
p = 1
}
s.progress = p
s.Refresh()
}
func (s *stripedProgress) CreateRenderer() fyne.WidgetRenderer {
bgRect := canvas.NewRectangle(s.bg)
fillRect := canvas.NewRectangle(applyAlpha(s.color, 180))
stripes := canvas.NewRaster(func(w, h int) image.Image {
img := image.NewRGBA(image.Rect(0, 0, w, h))
light := applyAlpha(s.color, 90)
dark := applyAlpha(s.color, 140)
for y := 0; y < h; y++ {
for x := 0; x < w; x++ {
// animate diagonal stripes using offset
if (((x + y) + int(s.offset)) / 6 % 2) == 0 {
img.Set(x, y, light)
} else {
img.Set(x, y, dark)
}
}
}
return img
})
objects := []fyne.CanvasObject{bgRect, fillRect, stripes}
r := &stripedProgressRenderer{
bar: s,
bg: bgRect,
fill: fillRect,
stripes: stripes,
objects: objects,
}
return r
}
type stripedProgressRenderer struct {
bar *stripedProgress
bg *canvas.Rectangle
fill *canvas.Rectangle
stripes *canvas.Raster
objects []fyne.CanvasObject
}
func (r *stripedProgressRenderer) Layout(size fyne.Size) {
r.bg.Resize(size)
r.bg.Move(fyne.NewPos(0, 0))
fillWidth := size.Width * r.bar.progress
fillSize := fyne.NewSize(fillWidth, size.Height)
r.fill.Resize(fillSize)
r.fill.Move(fyne.NewPos(0, 0))
r.stripes.Resize(fillSize)
r.stripes.Move(fyne.NewPos(0, 0))
}
func (r *stripedProgressRenderer) MinSize() fyne.Size {
return fyne.NewSize(120, 14)
}
func (r *stripedProgressRenderer) Refresh() {
// small drift to animate stripes
r.bar.offset += 1
r.Layout(r.bg.Size())
canvas.Refresh(r.bg)
}
func (r *stripedProgressRenderer) BackgroundColor() color.Color { return color.Transparent }
func (r *stripedProgressRenderer) Objects() []fyne.CanvasObject { return r.objects }
func (r *stripedProgressRenderer) Destroy() {}
func applyAlpha(c color.Color, alpha uint8) color.Color {
r, g, b, _ := c.RGBA()
return color.NRGBA{R: uint8(r >> 8), G: uint8(g >> 8), B: uint8(b >> 8), A: alpha}
}
// BuildQueueView creates the queue viewer UI
func BuildQueueView(
jobs []*queue.Job,
@ -126,10 +231,10 @@ func buildJobItem(
descLabel.Wrapping = fyne.TextWrapWord
// Progress bar (for running jobs)
progress := widget.NewProgressBar()
progress.SetValue(job.Progress / 100.0)
progress := newStripedProgress(moduleColor(job.Type))
progress.SetProgress(job.Progress / 100.0)
if job.Status == queue.JobStatusCompleted {
progress.SetValue(1.0)
progress.SetProgress(1.0)
}
progressWidget := progress

19
main.go
View File

@ -3790,9 +3790,9 @@ func (s *appState) executeUpscaleJob(ctx context.Context, job *queue.Job, progre
var s float64
if _, err := fmt.Sscanf(timeStr, "%d:%d:%f", &h, &m, &s); err == nil {
currentTime := float64(h*3600+m*60) + s
progress := currentTime / duration
if progress > 1.0 {
progress = 1.0
progress := (currentTime / duration) * 100.0
if progress > 100.0 {
progress = 100.0
}
if progressCallback != nil {
progressCallback(progress)
@ -3820,7 +3820,7 @@ func (s *appState) executeUpscaleJob(ctx context.Context, job *queue.Job, progre
}
if progressCallback != nil {
progressCallback(1)
progressCallback(100)
}
return nil
@ -10763,6 +10763,7 @@ func buildFiltersView(state *appState) fyne.CanvasObject {
// Top bar with module color
topBar := ui.TintedBar(filtersColor, container.NewHBox(backBtn, layout.NewSpacer(), queueBtn))
bottomBar := ui.TintedBar(filtersColor, container.NewHBox(state.statsBar, layout.NewSpacer()))
// Instructions
instructions := widget.NewLabel("Apply filters and color corrections to your video. Preview changes in real-time.")
@ -10910,7 +10911,7 @@ func buildFiltersView(state *appState) fyne.CanvasObject {
content := container.NewPadded(mainContent)
return container.NewBorder(topBar, nil, nil, nil, content)
return container.NewBorder(topBar, bottomBar, nil, nil, content)
}
// buildUpscaleView creates the Upscale module UI
@ -10932,6 +10933,7 @@ func buildUpscaleView(state *appState) fyne.CanvasObject {
// Top bar with module color
topBar := ui.TintedBar(upscaleColor, container.NewHBox(backBtn, layout.NewSpacer(), queueBtn))
bottomBar := ui.TintedBar(upscaleColor, container.NewHBox(state.statsBar, layout.NewSpacer()))
// Instructions
instructions := widget.NewLabel("Upscale your video to higher resolution using traditional or AI-powered methods.")
@ -11143,10 +11145,13 @@ func buildUpscaleView(state *appState) fyne.CanvasObject {
description += fmt.Sprintf(" + AI (%s)", state.upscaleAIModel)
}
desc := fmt.Sprintf("%s → %s", description, filepath.Base(outputPath))
return &queue.Job{
Type: queue.JobTypeUpscale,
Title: "Upscale: " + filepath.Base(state.upscaleFile.Path),
Description: description,
Description: desc,
OutputFile: outputPath,
Config: map[string]interface{}{
"inputPath": state.upscaleFile.Path,
"outputPath": outputPath,
@ -11224,7 +11229,7 @@ func buildUpscaleView(state *appState) fyne.CanvasObject {
content := container.NewPadded(mainContent)
return container.NewBorder(topBar, nil, nil, nil, content)
return container.NewBorder(topBar, bottomBar, nil, nil, content)
}
// checkAIUpscaleAvailable checks if Real-ESRGAN is available on the system