From 9fa8301ba6f4ae5c69796e2c87ad9dd9a59848ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petar=20Kapri=C5=A1?= Date: Tue, 30 Nov 2021 19:18:36 +0100 Subject: [PATCH] Split project into several packages The source code of the project has been split up into several packages, each one pertaining to different aspects of the system. Currently there is a main package, a drw package, containing all the funcitons regarding the drawing of the calendar, and a util package containing time-related project specific functions and general utility functions for the project. There are plenty of subsystems in the program which have not even been developed yet, and new packages will be made pertaining to those. The package tree may be heavily reorganized in the near future. --- Main.go | 294 ++++----------------------------------------------- drw/drw.go | 57 ++++++++++ drw/wall.go | 192 +++++++++++++++++++++++++++++++++ util/cal.go | 12 +++ util/util.go | 15 +++ 5 files changed, 298 insertions(+), 272 deletions(-) create mode 100644 drw/drw.go create mode 100644 drw/wall.go create mode 100644 util/cal.go create mode 100644 util/util.go diff --git a/Main.go b/Main.go index 8a87602..5213f30 100644 --- a/Main.go +++ b/Main.go @@ -3,293 +3,43 @@ package main import ( "log" "os" - "strconv" "time" - "unicode/utf8" "github.com/gdamore/tcell/v2" + + "gitlab.com/tvrdosrz/ck/drw" + "gitlab.com/tvrdosrz/ck/util" ) -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 lastDay(m time.Month) time.Time { - return date(selTime.Year(), m+1, 0) -} - -func monthHeight(m time.Month) int { - return (offset(m) + lastDay(m).Day() - 1) / 7 -} - -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 ; x+i < start; i++ { - scr.SetContent(x+i, y, ' ', nil, style) - } - for _, char := range s { - if i >= width { - break - } - - scr.SetContent(x+i, y, char, nil, style) - i++ - } - for ; x+i < start; i++ { - scr.SetContent(x+i, y, ' ', nil, style) - } -} - -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 bottomOfMonth(m time.Month, w time.Weekday) (time time.Time) { - last := lastDay(m) - day := last.Day() - - if w > last.Weekday() { - day -= 7 - } - - day += int(w - last.Weekday()) - - time = date(selTime.Year(), m, day) - return -} - -func topOfMonth(m time.Month, w time.Weekday) (time time.Time) { - first := date(selTime.Year(), m, 1) - day := 1 - - if w < first.Weekday() { - day += 7 - } - - day += int(w - first.Weekday()) - - time = date(selTime.Year(), m, day) - return -} - -func moveUp() { - weekday := selTime.Weekday() - newTime := selTime.AddDate(0, 0, -7) - if newTime.Month() == selTime.Month() { - selTime = newTime - } else if int(selTime.Month()) > monthsWide { - newMonth := selTime.Month() - time.Month(monthsWide) - selTime = bottomOfMonth(newMonth, weekday) - } -} - -func moveDown() { - weekday := selTime.Weekday() - newTime := selTime.AddDate(0, 0, +7) - if newTime.Month() == selTime.Month() { - selTime = newTime - } else if int(selTime.Month()) <= (12-1)/monthsWide*monthsWide { - newMonth := selTime.Month() + time.Month(monthsWide) - selTime = topOfMonth(newMonth, weekday) - } -} - -func offset(m time.Month) int { - return (int(date(selTime.Year(), m, 1).Weekday()) + 6) % 7 - -} - -func rowInMonth(m time.Month, day int) int { - return (day + offset(m) - 1) / 7 -} - -func leftmostInRow(m time.Month, row int) time.Time { - if row == 0 { - return date(selTime.Year(), m, 1) - } - if row > monthHeight(m) { - row = monthHeight(m) - } - - day := row*7 + 1 - offset(m) - - return date(selTime.Year(), m, day) -} - -func rightmostInRow(m time.Month, row int) time.Time { - if row >= monthHeight(m) { - return lastDay(m) - } - - day := row*7 + 7 - offset(m) - - return date(selTime.Year(), m, day) -} - -func moveRight() { - if selTime.Weekday() != time.Sunday && selTime.Day() != lastDay(selTime.Month()).Day() { - selTime = selTime.AddDate(0, 0, 1) - } else if int(selTime.Month())%monthsWide != 0 { - selTime = leftmostInRow(selTime.Month()+1, rowInMonth(selTime.Month(), selTime.Day())) - } -} - -func moveLeft() { - if selTime.Weekday() != time.Monday && selTime.Day() != 1 { - selTime = selTime.AddDate(0, 0, -1) - } else if int(selTime.Month())%monthsWide != 1 && monthsWide != 1 { // special case, if monthsWide - // is 1, (x%monthsWide) is 0 - selTime = rightmostInRow(selTime.Month()-1, rowInMonth(selTime.Month(), selTime.Day())) - } -} - -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 + util.Today = time.Now() + util.SelTime = util.Today var err error - wall, err = tcell.NewScreen() + drw.Wall, err = tcell.NewScreen() if err != nil { log.Fatalf("%+v", err) } - if err := wall.Init(); err != nil { + if err := drw.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) + drw.DefStyle = tcell.StyleDefault.Background(tcell.ColorReset).Foreground(tcell.ColorReset) + drw.TodayStyle = tcell.StyleDefault.Background(tcell.ColorReset).Foreground(tcell.GetColor("blue")).Bold(true).Reverse(true) + drw.SelStyle = tcell.StyleDefault.Background(tcell.ColorReset).Foreground(tcell.GetColor("green")).Bold(true) - wall.SetStyle(defStyle) - wall.Clear() - drawWall() + drw.Wall.SetStyle(drw.DefStyle) + drw.Wall.Clear() + drw.DrawWall() quit := func() { - wall.Fini() + drw.Wall.Fini() os.Exit(0) } for { // Update screen - wall.Show() + drw.Wall.Show() // Poll event - ev := wall.PollEvent() + ev := drw.Wall.PollEvent() // Process event switch ev := ev.(type) { @@ -301,17 +51,17 @@ func main() { } switch ev.Rune() { case 'h': - moveLeft() + drw.MoveLeft() case 'j': - moveDown() + drw.MoveDown() case 'k': - moveUp() + drw.MoveUp() case 'l': - moveRight() + drw.MoveRight() } } - drawWall() - wall.Sync() + drw.DrawWall() + drw.Wall.Sync() } } diff --git a/drw/drw.go b/drw/drw.go new file mode 100644 index 0000000..7d13d4c --- /dev/null +++ b/drw/drw.go @@ -0,0 +1,57 @@ +package drw + +import ( + "unicode/utf8" + + "github.com/gdamore/tcell/v2" + "gitlab.com/tvrdosrz/ck/util" +) + +var ( + months = [...]string{"", "Јануар", "Фебруар", "Март", "Април", "Мај", "Јун", + "Јул", "Август", "Септембар", "Октобар", "Новембар", "Децембар"} + weekdays = [...]string{"Недеља", "Понедељак", "Уторак", "Среда", "Четвртак", + "Петак", "Субота"} +) + +var ( + DefStyle tcell.Style + TodayStyle tcell.Style + SelStyle tcell.Style +) + +func centeredText(s string, x, y, width int, scr tcell.Screen, style tcell.Style) { + start := x + util.Max((width-utf8.RuneCountInString(s))/2, 0) + i := 0 + for ; x+i < start; i++ { + scr.SetContent(x+i, y, ' ', nil, style) + } + for _, char := range s { + if i >= width { + break + } + + scr.SetContent(x+i, y, char, nil, style) + i++ + } + for ; x+i < start; i++ { + scr.SetContent(x+i, y, ' ', nil, style) + } +} + +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++ + } +} diff --git a/drw/wall.go b/drw/wall.go new file mode 100644 index 0000000..84b2af3 --- /dev/null +++ b/drw/wall.go @@ -0,0 +1,192 @@ +package drw + +import ( + "strconv" + "time" + + "github.com/gdamore/tcell/v2" + + "gitlab.com/tvrdosrz/ck/util" +) + +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 ( + Wall tcell.Screen + + monthsWide int + monthsHigh int + monthsDrawn int +) + +func offset(m time.Month) int { + return (int(util.Date(util.SelTime.Year(), m, 1).Weekday()) + 6) % 7 + +} + +func lastDay(m time.Month) time.Time { + return util.Date(util.SelTime.Year(), m+1, 0) +} + +func monthHeight(m time.Month) int { + return (offset(m) + lastDay(m).Day() - 1) / 7 +} + +func bottomOfMonth(m time.Month, w time.Weekday) (time time.Time) { + last := lastDay(m) + day := last.Day() + + if w > last.Weekday() { + day -= 7 + } + + day += int(w - last.Weekday()) + + time = util.Date(util.SelTime.Year(), m, day) + return +} + +func topOfMonth(m time.Month, w time.Weekday) (time time.Time) { + first := util.Date(util.SelTime.Year(), m, 1) + day := 1 + + if w < first.Weekday() { + day += 7 + } + + day += int(w - first.Weekday()) + + time = util.Date(util.SelTime.Year(), m, day) + return +} + +func rowInMonth(m time.Month, day int) int { + return (day + offset(m) - 1) / 7 +} + +func leftmostInRow(m time.Month, row int) time.Time { + if row == 0 { + return util.Date(util.SelTime.Year(), m, 1) + } + if row > monthHeight(m) { + row = monthHeight(m) + } + + day := row*7 + 1 - offset(m) + + return util.Date(util.SelTime.Year(), m, day) +} + +func rightmostInRow(m time.Month, row int) time.Time { + if row >= monthHeight(m) { + return lastDay(m) + } + + day := row*7 + 7 - offset(m) + + return util.Date(util.SelTime.Year(), m, day) +} + +func MoveUp() { + weekday := util.SelTime.Weekday() + newTime := util.SelTime.AddDate(0, 0, -7) + if newTime.Month() == util.SelTime.Month() { + util.SelTime = newTime + } else if int(util.SelTime.Month()) > monthsWide { + newMonth := util.SelTime.Month() - time.Month(monthsWide) + util.SelTime = bottomOfMonth(newMonth, weekday) + } +} + +func MoveDown() { + weekday := util.SelTime.Weekday() + newTime := util.SelTime.AddDate(0, 0, +7) + if newTime.Month() == util.SelTime.Month() { + util.SelTime = newTime + } else if int(util.SelTime.Month()) <= (12-1)/monthsWide*monthsWide { + newMonth := util.SelTime.Month() + time.Month(monthsWide) + util.SelTime = topOfMonth(newMonth, weekday) + } +} + +func MoveRight() { + if util.SelTime.Weekday() != time.Sunday && util.SelTime.Day() != lastDay(util.SelTime.Month()).Day() { + util.SelTime = util.SelTime.AddDate(0, 0, 1) + } else if int(util.SelTime.Month())%monthsWide != 0 { + util.SelTime = leftmostInRow(util.SelTime.Month()+1, rowInMonth(util.SelTime.Month(), util.SelTime.Day())) + } +} + +func MoveLeft() { + if util.SelTime.Weekday() != time.Monday && util.SelTime.Day() != 1 { + util.SelTime = util.SelTime.AddDate(0, 0, -1) + } else if int(util.SelTime.Month())%monthsWide != 1 && monthsWide != 1 { // special case, if monthsWide + // is 1, (x%monthsWide) is 0 + util.SelTime = rightmostInRow(util.SelTime.Month()-1, rowInMonth(util.SelTime.Month(), util.SelTime.Day())) + } +} + +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 := util.Date(util.SelTime.Year(), m+1, 0).Day() + for i := 1; i <= dayNum; i++ { + weekday := int(util.Date(util.SelTime.Year(), m, i).Weekday()) + style := DefStyle + if util.Today.Day() == i && util.Today.Month() == m { + style = TodayStyle + } else if util.SelTime.Day() == i && util.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 = util.Min(monthsHigh*monthsWide, 12) + + startingMonth := time.Month((int(util.SelTime.Month())-1)/monthsDrawn*monthsDrawn + 1) + endingMonth := time.Month(util.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++ + } + } +} diff --git a/util/cal.go b/util/cal.go new file mode 100644 index 0000000..58281a2 --- /dev/null +++ b/util/cal.go @@ -0,0 +1,12 @@ +package util + +import "time" + +var ( + SelTime time.Time + Today time.Time +) + +func Date(year int, month time.Month, day int) time.Time { + return time.Date(year, month, day, 0, 0, 0, 0, time.Local) +} diff --git a/util/util.go b/util/util.go new file mode 100644 index 0000000..92b2fe7 --- /dev/null +++ b/util/util.go @@ -0,0 +1,15 @@ +package util + +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 +}