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.
This commit is contained in:
Petar Kapriš 2021-11-30 19:18:36 +01:00 committed by Петар Каприш
parent 1a194cc368
commit 9fa8301ba6
5 changed files with 298 additions and 272 deletions

294
Main.go
View file

@ -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()
}
}

57
drw/drw.go Normal file
View file

@ -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++
}
}

192
drw/wall.go Normal file
View file

@ -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++
}
}
}

12
util/cal.go Normal file
View file

@ -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)
}

15
util/util.go Normal file
View file

@ -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
}