77 "time"
88)
99
10- // TimerWidget is a widget displaying a timer
10+ // TimerWidget is a widget displaying a timer.
1111type TimerWidget struct {
1212 * BaseWidget
1313
@@ -26,29 +26,34 @@ type TimerWidget struct {
2626 data TimerData
2727}
2828
29+ // TimerData represents the current state of the timer.
2930type TimerData struct {
3031 startTime time.Time
3132 pausedTime time.Time
3233}
3334
35+ // IsPaused returns whether the timer is paused.
3436func (d * TimerData ) IsPaused () bool {
3537 return ! d .pausedTime .IsZero ()
3638}
3739
40+ // IsRunning returns whether the timer is running.
3841func (d * TimerData ) IsRunning () bool {
3942 return ! d .IsPaused () && d .HasDeadline ()
4043}
4144
45+ // HasDeadline returns whether the start time is set.
4246func (d * TimerData ) HasDeadline () bool {
4347 return ! d .startTime .IsZero ()
4448}
4549
50+ // Clear resets the state of the timer.
4651func (d * TimerData ) Clear () {
4752 d .startTime = time.Time {}
4853 d .pausedTime = time.Time {}
4954}
5055
51- // NewTimerWidget returns a new TimerWidget
56+ // NewTimerWidget returns a new TimerWidget.
5257func NewTimerWidget (bw * BaseWidget , opts WidgetConfig ) * TimerWidget {
5358 bw .setInterval (time .Duration (opts .Interval )* time .Millisecond , time .Second / 2 )
5459
@@ -73,6 +78,10 @@ func NewTimerWidget(bw *BaseWidget, opts WidgetConfig) *TimerWidget {
7378 times = append (times , defaultDuration )
7479 }
7580
81+ if len (formats ) == 0 {
82+ formats = append (formats , "%H:%i:%s" )
83+ }
84+
7685 layout := NewLayout (int (bw .dev .Pixels ))
7786 frames := layout .FormatLayout (frameReps , len (formats ))
7887
@@ -113,26 +122,30 @@ func (w *TimerWidget) Update() error {
113122 }
114123 size := int (w .dev .Pixels )
115124 img := image .NewRGBA (image .Rect (0 , 0 , size , size ))
116- var str string
117125
118126 for i := 0 ; i < len (w .formats ); i ++ {
127+ var timespan Timespan
119128 var fontColor = w .colors [i ]
120129
121130 if ! w .data .HasDeadline () {
122- str = Timespan (w .times [w .currIndex ]). Format ( w . formats [ i ], w . adaptive )
131+ timespan = Timespan (w .times [w .currIndex ])
123132 } else {
124133 remainingDuration := time .Until (w .data .startTime .Add (w .times [w .currIndex ]))
125- if remainingDuration < 0 && ! w .underflow {
126- str = Timespan (w .times [w .currIndex ]).Format (w .formats [i ], w .adaptive )
134+ if int (w .times [w .currIndex ].Seconds ()) == 0 {
135+ timespan = Timespan (remainingDuration * - 1 )
136+ } else if remainingDuration < 0 && ! w .underflow {
137+ timespan = Timespan (w .times [w .currIndex ])
127138 w .data .Clear ()
128139 } else if remainingDuration < 0 && w .underflow {
129140 fontColor = w .underflowColors [i ]
130- str = Timespan (remainingDuration * - 1 ). Format ( w . formats [ i ], w . adaptive )
141+ timespan = Timespan (remainingDuration * - 1 )
131142 } else {
132- str = Timespan (remainingDuration ). Format ( w . formats [ i ], w . adaptive )
143+ timespan = Timespan (remainingDuration )
133144 }
134145 }
146+
135147 font := fontByName (w .fonts [i ])
148+ str := timespan .Format (w .formats [i ], w .adaptive )
136149
137150 drawString (img ,
138151 w .frames [i ],
@@ -147,10 +160,11 @@ func (w *TimerWidget) Update() error {
147160 return w .render (w .dev , img )
148161}
149162
163+ // Timespan represents the duration between two events.
150164type Timespan time.Duration
151165
166+ // Format returns the formatted version of the timespan.
152167func (t Timespan ) Format (format string , adaptive bool ) string {
153- formatStr := format
154168 tm := map [string ]string {
155169 "%h" : "03" ,
156170 "%H" : "15" ,
@@ -162,44 +176,51 @@ func (t Timespan) Format(format string, adaptive bool) string {
162176
163177 z := time .Unix (0 , 0 ).UTC ()
164178 current := z .Add (time .Duration (t ))
165- foundNonZero := false
166- timeStr := ""
179+ var timeStr string
167180 if adaptive {
168- for i := 0 ; i < len (formatStr ); i ++ {
169- if formatStr [i :i + 1 ] == "%" && len (formatStr ) > i + 1 {
170- format := ReplaceAll (formatStr [i :i + 2 ], tm )
171- str := strings .TrimLeft (current .Format (format ), "0" )
172- timeStr += str
173- if str != "" {
174- format = ReplaceAll (formatStr [i + 2 :], tm )
175- timeStr += current .Format (format )
176- break
177- }
178- foundNonZero = true
179- i ++
180- } else {
181- if ! foundNonZero {
182- timeStr += formatStr [i : i + 1 ]
183- }
184- }
185- }
186- if timeStr == "" {
187- timeStr = "0"
188- }
181+ timeStr = TrimTime (current , format , tm )
189182 } else {
190183 format := ReplaceAll (format , tm )
191184 timeStr = current .Format (format )
192185 }
193186 return timeStr
194187}
195188
189+ // TrimTime does remove leading zeroes and separator that are not required for the time representation.
190+ func TrimTime (current time.Time , formatStr string , tm map [string ]string ) string {
191+ foundNonZero := false
192+ timeStr := ""
193+ for i := 0 ; i < len (formatStr ); i ++ {
194+ if formatStr [i :i + 1 ] == "%" && len (formatStr ) > i + 1 {
195+ format := ReplaceAll (formatStr [i :i + 2 ], tm )
196+ str := strings .TrimLeft (current .Format (format ), "0" )
197+ timeStr += str
198+ if str != "" {
199+ format = ReplaceAll (formatStr [i + 2 :], tm )
200+ timeStr += current .Format (format )
201+ break
202+ }
203+ foundNonZero = true
204+ i ++
205+ } else if ! foundNonZero {
206+ timeStr += formatStr [i : i + 1 ]
207+ }
208+ }
209+ if timeStr == "" {
210+ return "0"
211+ }
212+ return timeStr
213+ }
214+
215+ // ReplaceAll does a replacement with all entries of a map.
196216func ReplaceAll (str string , tm map [string ]string ) string {
197217 for k , v := range tm {
198218 str = strings .ReplaceAll (str , k , v )
199219 }
200220 return str
201221}
202222
223+ // TriggerAction updates the timer state.
203224func (w * TimerWidget ) TriggerAction (hold bool ) {
204225 if hold {
205226 if w .data .IsPaused () {
@@ -211,7 +232,7 @@ func (w *TimerWidget) TriggerAction(hold bool) {
211232 if w .data .IsRunning () {
212233 w .data .pausedTime = time .Now ()
213234 } else if w .data .IsPaused () && w .data .HasDeadline () {
214- pausedDuration := time .Now (). Sub (w .data .pausedTime )
235+ pausedDuration := time .Since (w .data .pausedTime )
215236 w .data .startTime = w .data .startTime .Add (pausedDuration )
216237 w .data .pausedTime = time.Time {}
217238 } else {
0 commit comments