123 lines
2.0 KiB
Go
123 lines
2.0 KiB
Go
// SPDX-License-Identifier: Unlicense OR MIT
|
|
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// Timer implements a timer with adjustable duration.
|
|
type Timer struct {
|
|
// Updated is used to notify UI about changes in the timer.
|
|
Updated chan struct{}
|
|
|
|
mu sync.Mutex
|
|
start time.Time
|
|
now time.Time
|
|
duration time.Duration
|
|
}
|
|
|
|
// NewTimer creates a new timer with the specified duration.
|
|
func NewTimer(initialDuration time.Duration) *Timer {
|
|
return &Timer{
|
|
Updated: make(chan struct{}),
|
|
duration: initialDuration,
|
|
}
|
|
}
|
|
|
|
// Start the timer goroutine and return a cancel func.
|
|
func (t *Timer) Start() context.CancelFunc {
|
|
now := time.Now()
|
|
t.now = now
|
|
t.start = now
|
|
|
|
done := make(chan struct{})
|
|
go t.run(done)
|
|
return func() { close(done) }
|
|
}
|
|
|
|
func (t *Timer) run(done chan struct{}) {
|
|
tick := time.NewTicker(50 * time.Millisecond)
|
|
defer tick.Stop()
|
|
|
|
for {
|
|
select {
|
|
case now := <-tick.C:
|
|
t.update(now)
|
|
case <-done:
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (t *Timer) invalidate() {
|
|
select {
|
|
case t.Updated <- struct{}{}:
|
|
default:
|
|
}
|
|
}
|
|
|
|
func (t *Timer) update(now time.Time) {
|
|
t.mu.Lock()
|
|
defer t.mu.Unlock()
|
|
|
|
previousNow := t.now
|
|
t.now = now
|
|
|
|
progressAfter := t.now.Sub(t.start)
|
|
if progressAfter <= t.duration {
|
|
t.invalidate()
|
|
return
|
|
}
|
|
|
|
progressBefore := previousNow.Sub(t.start)
|
|
if progressBefore <= t.duration {
|
|
t.invalidate()
|
|
return
|
|
}
|
|
}
|
|
|
|
// Reset resets the timer.
|
|
func (t *Timer) Reset() {
|
|
t.mu.Lock()
|
|
defer t.mu.Unlock()
|
|
|
|
t.start = t.now
|
|
t.invalidate()
|
|
}
|
|
|
|
// SetDuration changes the duration.
|
|
func (t *Timer) SetDuration(duration time.Duration) {
|
|
t.mu.Lock()
|
|
defer t.mu.Unlock()
|
|
|
|
if t.duration == duration {
|
|
return
|
|
}
|
|
t.duration = duration
|
|
t.invalidate()
|
|
}
|
|
|
|
// Info returns timer information.
|
|
type Info struct {
|
|
Progress time.Duration
|
|
Duration time.Duration
|
|
}
|
|
|
|
// Info returns the current timer state.
|
|
func (t *Timer) Info() Info {
|
|
t.mu.Lock()
|
|
defer t.mu.Unlock()
|
|
|
|
info := Info{
|
|
Progress: t.now.Sub(t.start),
|
|
Duration: t.duration,
|
|
}
|
|
if info.Progress > info.Duration {
|
|
info.Progress = info.Duration
|
|
}
|
|
return info
|
|
}
|