package main import ( "log" "os" "strconv" "time" "unicode/utf8" "github.com/gdamore/tcell/v2" ) const ( dayWidth = 2 smallGap = 1 largeGap = 3 maxMonthWidth = 7*dayWidth + 6*smallGap maxMonthHeight = (31+6+(7-1))/7 + 2 /* maxMonthHeight is the height of a 31-day month starting on Sunday (month name and weekdays included) */ titleHeight = smallGap + 1 ) var ( months = [...]string{"", "Јануар", "Фебруар", "Март", "Април", "Мај", "Јун", "Јул", "Август", "Септембар", "Октобар", "Новембар", "Децембар"} weekdays = [...]string{"Недеља", "Понедељак", "Уторак", "Среда", "Четвртак", "Петак", "Субота"} ) var ( wall tcell.Screen defStyle tcell.Style todayStyle tcell.Style selStyle tcell.Style monthsWide int monthsHigh int monthsDrawn int ) var ( selTime time.Time today time.Time ) func max(a, b int) int { if a > b { return a } return b } func min(a, b int) int { if a < b { return a } return b } func date(year int, month time.Month, day int) time.Time { return time.Date(year, month, day, 0, 0, 0, 0, time.Local) } func centeredText(s string, x, y, width int, scr tcell.Screen, style tcell.Style) { start := x + max((width-utf8.RuneCountInString(s))/2, 0) i := 0 for _, char := range s { if i >= width { break } scr.SetContent(start+i, y, char, nil, style) i++ } } func wrappedText(s string, scr tcell.Screen, style tcell.Style) { x := 0 y := 0 w, h := scr.Size() for _, char := range s { if x >= w { if y >= h { break } x = 0 y++ } scr.SetContent(x, y, char, nil, style) x++ } } func drawMonth(m time.Month, x, y int) { centeredText(months[m], x, y, maxMonthWidth, wall, defStyle) for i := 1; i <= 7; i++ { centeredText(weekdays[i%7], x+(i-1)*(dayWidth+smallGap), y+1, dayWidth, wall, defStyle) } dayNum := date(selTime.Year(), m+1, 0).Day() for i := 1; i <= dayNum; i++ { weekday := int(date(selTime.Year(), m, i).Weekday()) style := defStyle if today.Day() == i && today.Month() == m { style = todayStyle } else if selTime.Day() == i && selTime.Month() == m { style = selStyle } centeredText( strconv.Itoa(i), x+((weekday-1+7)%7)*(dayWidth+smallGap), y+2, 2, wall, style) if weekday == 0 { y++ } } } func drawWall() { wall.Clear() w, h := wall.Size() if w < maxMonthWidth+2*smallGap || h < maxMonthHeight+2*smallGap+titleHeight { wrappedText("Екран је премали за календар.", wall, defStyle) return } monthsWide = (w - 2*smallGap + largeGap) / (maxMonthWidth + largeGap) monthsHigh = (h - smallGap - titleHeight) / (maxMonthHeight + smallGap) monthsDrawn = min(monthsHigh*monthsWide, 12) startingMonth := time.Month((int(selTime.Month())-1)/monthsDrawn*monthsDrawn + 1) endingMonth := time.Month(min(12, int(startingMonth)+monthsDrawn-1)) centerVert := (w - monthsWide*maxMonthWidth - (monthsWide-1)*largeGap) / 2 for i, m := 0, startingMonth; i < monthsHigh; i++ { for j := 0; j < monthsWide; j++ { drawMonth(m, centerVert+j*(maxMonthWidth+largeGap), titleHeight+smallGap+i*(maxMonthHeight+smallGap)) if m >= endingMonth { return } m++ } } } func main() { today = time.Now() selTime = today var err error wall, err = tcell.NewScreen() if err != nil { log.Fatalf("%+v", err) } if err := wall.Init(); err != nil { log.Fatalf("%+v", err) } defStyle = tcell.StyleDefault.Background(tcell.ColorReset).Foreground(tcell.ColorReset) todayStyle = tcell.StyleDefault.Background(tcell.ColorReset).Foreground(tcell.GetColor("blue")).Bold(true).Reverse(true) selStyle = tcell.StyleDefault.Background(tcell.ColorReset).Foreground(tcell.GetColor("green")).Bold(true) wall.SetStyle(defStyle) wall.Clear() drawWall() quit := func() { wall.Fini() os.Exit(0) } for { // Update screen wall.Show() // Poll event ev := wall.PollEvent() // Process event switch ev := ev.(type) { case *tcell.EventResize: drawWall() wall.Sync() case *tcell.EventKey: if ev.Key() == tcell.KeyEscape || ev.Key() == tcell.KeyCtrlC || ev.Rune() == 'Q' || ev.Rune() == 'q' { quit() } } } }