VideoTools/vendor/fyne.io/fyne/v2/widget/calendar.go
Stu Leak 68df790d27 Fix player frame generation and video playback
Major improvements to UnifiedPlayer:

1. GetFrameImage() now works when paused for responsive UI updates
2. Play() method properly starts FFmpeg process
3. Frame display loop runs continuously for smooth video display
4. Disabled audio temporarily to fix video playback fundamentals
5. Simplified FFmpeg command to focus on video stream only

Player now:
- Generates video frames correctly
- Shows video when paused
- Has responsive progress tracking
- Starts playback properly

Next steps: Re-enable audio playback once video is stable
2026-01-07 22:20:00 -05:00

241 lines
6.0 KiB
Go

package widget
import (
"math"
"strconv"
"strings"
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/lang"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
)
// Declare conformity with Layout interface
var _ fyne.Layout = (*calendarLayout)(nil)
const (
daysPerWeek = 7
maxWeeksPerMonth = 6
)
var minCellContent = NewLabel("22")
// Calendar creates a new date time picker which returns a time object
//
// Since: 2.6
type Calendar struct {
BaseWidget
currentTime time.Time
monthPrevious *Button
monthNext *Button
monthLabel *Label
dates *fyne.Container
OnChanged func(time.Time) `json:"-"`
}
// NewCalendar creates a calendar instance
//
// Since: 2.6
func NewCalendar(cT time.Time, changed func(time.Time)) *Calendar {
c := &Calendar{
currentTime: cT,
OnChanged: changed,
}
c.ExtendBaseWidget(c)
return c
}
// CreateRenderer returns a new WidgetRenderer for this widget.
// This should not be called by regular code, it is used internally to render a widget.
func (c *Calendar) CreateRenderer() fyne.WidgetRenderer {
c.monthPrevious = NewButtonWithIcon("", theme.NavigateBackIcon(), func() {
c.currentTime = c.currentTime.AddDate(0, -1, 0)
// Dates are 'normalised', forcing date to start from the start of the month ensures move from March to February
c.currentTime = time.Date(c.currentTime.Year(), c.currentTime.Month(), 1, 0, 0, 0, 0, c.currentTime.Location())
c.monthLabel.SetText(c.monthYear())
c.dates.Objects = c.calendarObjects()
})
c.monthPrevious.Importance = LowImportance
c.monthNext = NewButtonWithIcon("", theme.NavigateNextIcon(), func() {
c.currentTime = c.currentTime.AddDate(0, 1, 0)
c.monthLabel.SetText(c.monthYear())
c.dates.Objects = c.calendarObjects()
})
c.monthNext.Importance = LowImportance
c.monthLabel = NewLabel(c.monthYear())
nav := &fyne.Container{
Layout: layout.NewBorderLayout(nil, nil, c.monthPrevious, c.monthNext),
Objects: []fyne.CanvasObject{
c.monthPrevious, c.monthNext,
&fyne.Container{Layout: layout.NewCenterLayout(), Objects: []fyne.CanvasObject{c.monthLabel}},
},
}
c.dates = &fyne.Container{Layout: newCalendarLayout(), Objects: c.calendarObjects()}
dateContainer := &fyne.Container{
Layout: layout.NewBorderLayout(nav, nil, nil, nil),
Objects: []fyne.CanvasObject{nav, c.dates},
}
return NewSimpleRenderer(dateContainer)
}
func (c *Calendar) calendarObjects() []fyne.CanvasObject {
offset := 0
switch getLocaleWeekStart() {
case "Saturday":
offset = 6
case "Sunday":
default:
offset = 1
}
var columnHeadings []fyne.CanvasObject
for i := 0; i < daysPerWeek; i++ {
t := NewLabel(shortDayName(time.Weekday((i + offset) % daysPerWeek).String()))
t.Alignment = fyne.TextAlignCenter
columnHeadings = append(columnHeadings, t)
}
return append(columnHeadings, c.daysOfMonth()...)
}
func (c *Calendar) dateForButton(dayNum int) time.Time {
oldName, off := c.currentTime.Zone()
return time.Date(c.currentTime.Year(), c.currentTime.Month(), dayNum, c.currentTime.Hour(), c.currentTime.Minute(), 0, 0, time.FixedZone(oldName, off)).In(c.currentTime.Location())
}
func (c *Calendar) daysOfMonth() []fyne.CanvasObject {
start := time.Date(c.currentTime.Year(), c.currentTime.Month(), 1, 0, 0, 0, 0, c.currentTime.Location())
var buttons []fyne.CanvasObject
dayIndex := int(start.Weekday())
// account for Go time pkg starting on sunday at index 0
switch getLocaleWeekStart() {
case "Saturday":
if dayIndex == daysPerWeek-1 {
dayIndex = 0
} else {
dayIndex++
}
case "Sunday": // nothing to do
default:
if dayIndex == 0 {
dayIndex += daysPerWeek - 1
} else {
dayIndex--
}
}
// add spacers if week doesn't start on Monday
for i := 0; i < dayIndex; i++ {
buttons = append(buttons, layout.NewSpacer())
}
for d := start; d.Month() == start.Month(); d = d.AddDate(0, 0, 1) {
dayNum := d.Day()
s := strconv.Itoa(dayNum)
b := NewButton(s, func() {
selectedDate := c.dateForButton(dayNum)
c.OnChanged(selectedDate)
})
b.Importance = LowImportance
buttons = append(buttons, b)
}
return buttons
}
func (c *Calendar) monthYear() string {
return c.currentTime.Format("January 2006")
}
type calendarLayout struct {
cellSize fyne.Size
}
func newCalendarLayout() fyne.Layout {
return &calendarLayout{}
}
// Layout is called to pack all child objects into a specified size.
// For a calendar grid this will pack objects into a table format.
func (g *calendarLayout) Layout(objects []fyne.CanvasObject, size fyne.Size) {
weeks := 1
day := 0
for i, child := range objects {
if !child.Visible() {
continue
}
if day%daysPerWeek == 0 && i >= daysPerWeek {
weeks++
}
day++
}
g.cellSize = fyne.NewSize(size.Width/float32(daysPerWeek),
size.Height/float32(weeks))
row, col := 0, 0
i := 0
for _, child := range objects {
if !child.Visible() {
continue
}
lead := g.getLeading(row, col)
trail := g.getTrailing(row, col)
child.Move(lead)
child.Resize(fyne.NewSize(trail.X, trail.Y).Subtract(lead))
if (i+1)%daysPerWeek == 0 {
row++
col = 0
} else {
col++
}
i++
}
}
// MinSize sets the minimum size for the calendar
func (g *calendarLayout) MinSize(_ []fyne.CanvasObject) fyne.Size {
pad := theme.Padding()
largestMin := minCellContent.MinSize()
return fyne.NewSize(largestMin.Width*daysPerWeek+pad*(daysPerWeek-1),
largestMin.Height*maxWeeksPerMonth+pad*(maxWeeksPerMonth-1))
}
// Get the leading edge position of a grid cell.
// The row and col specify where the cell is in the calendar.
func (g *calendarLayout) getLeading(row, col int) fyne.Position {
x := (g.cellSize.Width) * float32(col)
y := (g.cellSize.Height) * float32(row)
return fyne.NewPos(float32(math.Round(float64(x))), float32(math.Round(float64(y))))
}
// Get the trailing edge position of a grid cell.
// The row and col specify where the cell is in the calendar.
func (g *calendarLayout) getTrailing(row, col int) fyne.Position {
return g.getLeading(row+1, col+1)
}
func shortDayName(in string) string {
lower := strings.ToLower(in)
key := lower + ".short"
long := lang.X(lower, in)
return strings.ToUpper(lang.X(key, long[:3]))
}