fckeuspy-go/vendor/fyne.io/fyne/v2/widget/calendar.go

235 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]))
}