From 2b9f17b61245efbfee19e43a7f89db2ff36aa1c9 Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Tue, 7 May 2024 01:30:06 -0400 Subject: [PATCH] Add clock example --- examples/clock/main.go | 126 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 examples/clock/main.go diff --git a/examples/clock/main.go b/examples/clock/main.go new file mode 100644 index 0000000..f4beb7f --- /dev/null +++ b/examples/clock/main.go @@ -0,0 +1,126 @@ +// 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) +}