Add calendar widget
This commit is contained in:
parent
c06f10c193
commit
f778ef5c95
167
calendar.go
Normal file
167
calendar.go
Normal file
@ -0,0 +1,167 @@
|
||||
package objects
|
||||
|
||||
import "fmt"
|
||||
import "time"
|
||||
import "git.tebibyte.media/tomo/tomo"
|
||||
import "git.tebibyte.media/tomo/tomo/event"
|
||||
import "git.tebibyte.media/tomo/objects/layouts"
|
||||
|
||||
// Calendar is an object that can display a date and allow the user to change
|
||||
// it. It can display one month at a time.
|
||||
type Calendar struct {
|
||||
tomo.ContainerBox
|
||||
|
||||
grid tomo.ContainerBox
|
||||
time time.Time
|
||||
monthLabel *Label
|
||||
|
||||
on struct {
|
||||
edit event.FuncBroadcaster
|
||||
}
|
||||
}
|
||||
|
||||
// NewCalendar creates a new calendar with the specified date.
|
||||
func NewCalendar (tm time.Time) *Calendar {
|
||||
calendar := &Calendar {
|
||||
ContainerBox: tomo.NewContainerBox(),
|
||||
time: tm,
|
||||
}
|
||||
calendar.SetRole(tomo.R("objects", "Calendar", ""))
|
||||
calendar.SetLayout(layouts.ContractVertical)
|
||||
|
||||
prevButton := NewButton("")
|
||||
prevButton.SetIcon(tomo.IconGoPrevious)
|
||||
prevButton.OnClick(func () {
|
||||
calendar.prevMonth()
|
||||
calendar.on.edit.Broadcast()
|
||||
})
|
||||
nextButton := NewButton("")
|
||||
nextButton.SetIcon(tomo.IconGoNext)
|
||||
nextButton.OnClick(func () {
|
||||
calendar.nextMonth()
|
||||
calendar.on.edit.Broadcast()
|
||||
})
|
||||
calendar.monthLabel = NewLabel("")
|
||||
calendar.monthLabel.SetAlign(tomo.AlignMiddle, tomo.AlignMiddle)
|
||||
|
||||
calendar.grid = tomo.NewContainerBox()
|
||||
calendar.grid.SetRole(tomo.R("objects", "CalendarGrid", ""))
|
||||
calendar.grid.SetLayout(layouts.NewGrid (
|
||||
[]bool { true, true, true, true, true, true, true },
|
||||
[]bool { }))
|
||||
calendar.Add(NewInnerContainer (
|
||||
layouts.Row { false, true, false },
|
||||
prevButton, calendar.monthLabel, nextButton))
|
||||
calendar.Add(calendar.grid)
|
||||
|
||||
calendar.OnScroll(calendar.handleScroll)
|
||||
calendar.CaptureScroll(true)
|
||||
|
||||
calendar.refresh()
|
||||
return calendar
|
||||
}
|
||||
|
||||
// SetTime sets the date the calendar will display.
|
||||
func (this *Calendar) SetTime (tm time.Time) {
|
||||
if this.time == tm { return }
|
||||
this.time = tm
|
||||
this.refresh()
|
||||
}
|
||||
|
||||
// OnEdit sets a function to be called when the user changes the date on the
|
||||
// calendar.
|
||||
func (this *Calendar) OnEdit (callback func ()) {
|
||||
this.on.edit.Connect(callback)
|
||||
}
|
||||
|
||||
func (this *Calendar) prevMonth () {
|
||||
this.time = firstOfMonth(this.time.Add(24 * time.Hour * 40))
|
||||
this.refresh()
|
||||
}
|
||||
|
||||
func (this *Calendar) nextMonth () {
|
||||
this.time = firstOfMonth(this.time.Add(24 * time.Hour * -20))
|
||||
this.refresh()
|
||||
}
|
||||
|
||||
var weekdayAbbreviations = []string {
|
||||
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
|
||||
}
|
||||
|
||||
func (this *Calendar) refresh () {
|
||||
this.monthLabel.SetText(this.time.Format("2006 January"))
|
||||
|
||||
this.grid.Clear()
|
||||
for _, day := range weekdayAbbreviations {
|
||||
dayLabel := tomo.NewTextBox()
|
||||
dayLabel.SetRole(tomo.R("objects", "CalendarWeekdayHeader", ""))
|
||||
dayLabel.SetText(day)
|
||||
dayLabel.SetAlign(tomo.AlignMiddle, tomo.AlignMiddle)
|
||||
this.grid.Add(dayLabel)
|
||||
}
|
||||
|
||||
dayIter := 0 - int(firstOfMonth(this.time).Weekday())
|
||||
if dayIter <= -6 {
|
||||
dayIter = 1
|
||||
}
|
||||
weekday := 0
|
||||
totalDays := daysInMonth(this.time)
|
||||
for ; dayIter <= totalDays; dayIter ++ {
|
||||
weekday = (weekday + 1) % 7
|
||||
if dayIter > 0 {
|
||||
day := tomo.NewTextBox()
|
||||
day.SetText(fmt.Sprint(dayIter))
|
||||
if weekday == 1 || weekday == 0 {
|
||||
day.SetRole(tomo.R("objects", "CalendarDay", "weekend"))
|
||||
} else {
|
||||
day.SetRole(tomo.R("objects", "CalendarDay", "weekday"))
|
||||
}
|
||||
this.grid.Add(day)
|
||||
} else {
|
||||
this.grid.Add(tomo.NewBox())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Calendar) handleScroll (x, y float64) {
|
||||
if y < 0 {
|
||||
this.nextMonth()
|
||||
} else {
|
||||
this.prevMonth()
|
||||
}
|
||||
}
|
||||
|
||||
func firstOfMonth (tm time.Time) time.Time {
|
||||
return time.Date(tm.Year(), tm.Month(), 0, 0, 0, 0, 0, time.Local)
|
||||
}
|
||||
|
||||
func daysInMonth (tm time.Time) (days int) {
|
||||
year := tm.Year()
|
||||
month := tm.Month()
|
||||
switch month {
|
||||
case 1: days = 31
|
||||
case 2:
|
||||
// betcha didn't know this about leap years
|
||||
if year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) {
|
||||
days = 29
|
||||
} else {
|
||||
days = 28
|
||||
}
|
||||
case 3: days = 31
|
||||
case 4: days = 30
|
||||
case 5: days = 31
|
||||
case 6: days = 30
|
||||
case 7: days = 31
|
||||
case 8: days = 31
|
||||
case 9: days = 30
|
||||
case 10: days = 31
|
||||
case 11: days = 30
|
||||
case 12: days = 31
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func canonMonth (tm time.Time) int {
|
||||
return int(tm.Month() - 1) + tm.Year() * 12
|
||||
}
|
Loading…
Reference in New Issue
Block a user