// Example clock demonstrates the use of goroutines and tomo.Do() to provide // live-updating information. package main import "time" import "math" import "image" import "image/color" import "git.tebibyte.media/tomo/tomo" import "git.tebibyte.media/tomo/nasin" import "git.tebibyte.media/tomo/objects" import "git.tebibyte.media/tomo/tomo/theme" import "git.tebibyte.media/tomo/tomo/canvas" import "git.tebibyte.media/tomo/objects/layouts" type Application struct { clockFace *ClockFace timeLabel *objects.Label } func (this *Application) Describe () nasin.ApplicationDescription { return nasin.ApplicationDescription { Name: "Clock", ID: "xyz.holanet.TomoClockExample", } } func (this *Application) Init () error { window, err := nasin.NewApplicationWindow(this, image.Rect(0, 0, 128, 160)) if err != nil { return err } this.clockFace = NewClockFace() this.timeLabel = objects.NewLabel("") this.timeLabel.SetAlign(tomo.AlignMiddle, tomo.AlignMiddle) container := objects.NewOuterContainer ( layouts.NewGrid([]bool { true }, []bool { true, false }), this.clockFace, this.timeLabel) window.SetRoot(container) go func () { for { tomo.Do(this.updateTime) time.Sleep(time.Second) } } () window.OnClose(tomo.Stop) window.Show() return nil } func (this *Application) updateTime () { now := time.Now() this.clockFace.SetTime(now) this.timeLabel.SetText(now.Format(time.DateTime)) } func main () { nasin.RunApplication(&Application { }) } type ClockFace struct { tomo.CanvasBox time time.Time } func NewClockFace () *ClockFace { box := &ClockFace { CanvasBox: tomo.NewCanvasBox(), } theme.Apply(box, theme.R("nasin", "ClockFace", "")) box.SetDrawer(box) return box } // TODO move ClockFace to objects func (this *ClockFace) SetTime (when time.Time) { this.time = when this.Invalidate() } func (this *ClockFace) Draw (destination canvas.Canvas) { pen := destination.Pen() pen.Fill(color.Transparent) pen.Rectangle(destination.Bounds()) for hour := 0; hour < 12; hour ++ { radialLine ( destination, theme.ColorForeground, 0.8, 0.9, float64(hour) / 6 * math.Pi) } second := float64(this.time.Second()) minute := float64(this.time.Minute()) + second / 60 hour := float64(this.time.Hour()) + minute / 60 radialLine(destination, theme.ColorForeground, 0, 0.5, (hour - 3) / 6 * math.Pi) radialLine(destination, theme.ColorForeground, 0, 0.7, (minute - 15) / 30 * math.Pi) radialLine(destination, theme.ColorAccent, 0, 0.7, (second - 15) / 30 * math.Pi) } func radialLine ( destination canvas.Canvas, source color.Color, inner float64, outer float64, theta float64, ) { pen := destination.Pen() bounds := destination.Bounds() width := float64(bounds.Dx()) / 2 height := float64(bounds.Dy()) / 2 min := bounds.Min.Add(image.Pt ( int(math.Cos(theta) * inner * width + width), int(math.Sin(theta) * inner * height + height))) max := bounds.Min.Add(image.Pt ( int(math.Cos(theta) * outer * width + width), int(math.Sin(theta) * outer * height + height))) pen.Stroke(source) pen.StrokeWeight(1) pen.Path(min, max) }