129 lines
3.3 KiB
Go
129 lines
3.3 KiB
Go
package timer
|
|
|
|
import (
|
|
"image"
|
|
"sync"
|
|
"time"
|
|
|
|
"gioui.org/app"
|
|
"gioui.org/io/event"
|
|
"gioui.org/layout"
|
|
"gioui.org/op"
|
|
"gioui.org/op/clip"
|
|
"gioui.org/op/paint"
|
|
"gioui.org/unit"
|
|
"gioui.org/widget"
|
|
"gioui.org/widget/material"
|
|
"git.mstar.dev/mstar/goutils/other"
|
|
"git.mstar.dev/mstar/timer/shared"
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
type StateEnterTime struct {
|
|
initialSetupDone bool
|
|
editor *widget.Editor
|
|
inputField *material.EditorStyle
|
|
inputErrorMessage *string
|
|
ops op.Ops
|
|
}
|
|
|
|
func (s *StateEnterTime) initialSetup(globalState *shared.GlobalState) {
|
|
s.editor = &widget.Editor{
|
|
SingleLine: true,
|
|
Submit: true,
|
|
Filter: "01234567890TZ+-:",
|
|
}
|
|
field := material.Editor(globalState.Theme, s.editor, "timestamp")
|
|
s.inputField = &field
|
|
s.initialSetupDone = true
|
|
globalState.Window.Option(func(m unit.Metric, c *app.Config) {
|
|
c.MaxSize.X = 520
|
|
c.MaxSize.Y = 150
|
|
c.MinSize = c.MaxSize
|
|
c.Size = c.MaxSize
|
|
})
|
|
}
|
|
|
|
func (s *StateEnterTime) Run(globalState *shared.GlobalState, event event.Event) shared.NewState {
|
|
if !s.initialSetupDone {
|
|
s.initialSetup(globalState)
|
|
}
|
|
switch event := event.(type) {
|
|
case app.FrameEvent:
|
|
return s.handleFrameEvent(globalState, &event)
|
|
case app.DestroyEvent:
|
|
return s.handleQuitEvent()
|
|
case app.WaylandViewEvent, app.X11ViewEvent:
|
|
return shared.EmptyEvent()
|
|
default:
|
|
log.Debug().Any("event", event).Type("type", event).Msg("Unknown event")
|
|
return shared.NewState{}
|
|
}
|
|
}
|
|
|
|
func (s *StateEnterTime) Exit(globalState *shared.GlobalState, wg *sync.WaitGroup) {
|
|
wg.Done()
|
|
}
|
|
|
|
func (s *StateEnterTime) handleFrameEvent(
|
|
globalState *shared.GlobalState,
|
|
frameEvent *app.FrameEvent,
|
|
) shared.NewState {
|
|
gtx := app.NewContext(&s.ops, *frameEvent)
|
|
for ev, ok := s.editor.Update(gtx); ok; ev, ok = s.editor.Update(gtx) {
|
|
switch ev := ev.(type) {
|
|
case widget.SubmitEvent:
|
|
t, err := time.Parse(time.RFC3339, ev.Text)
|
|
if err == nil {
|
|
return s.handleTimeEntered(t)
|
|
}
|
|
s.inputErrorMessage = other.IntoPointer(err.Error())
|
|
log.Debug().Err(err).Msg("Invalid timestamp")
|
|
}
|
|
log.Debug().Any("Editor event", ev).Type("type", ev).Send()
|
|
}
|
|
paint.FillShape(
|
|
gtx.Ops,
|
|
globalState.Theme.Bg,
|
|
clip.Rect(image.Rect(0, 0, gtx.Constraints.Max.X, gtx.Constraints.Max.Y)).Op(),
|
|
)
|
|
label := material.H3(globalState.Theme, "Enter timestamp")
|
|
hint := material.Subtitle1(
|
|
globalState.Theme,
|
|
"Format: <YYYY>-<MM>-<DD>T<hh>:<mm>:<ss>(Z|+<TZ>)",
|
|
)
|
|
var errorMessage material.LabelStyle
|
|
nrOfElements := 3
|
|
if s.inputErrorMessage != nil {
|
|
nrOfElements++
|
|
errorMessage = material.Subtitle2(globalState.Theme, *s.inputErrorMessage)
|
|
}
|
|
list := layout.List{Axis: layout.Vertical}
|
|
list.Layout(gtx, nrOfElements, func(gtx layout.Context, index int) layout.Dimensions {
|
|
switch index {
|
|
case 0:
|
|
return label.Layout(gtx)
|
|
case 1:
|
|
return hint.Layout(gtx)
|
|
case 2:
|
|
return s.inputField.Layout(gtx)
|
|
case 3:
|
|
return errorMessage.Layout(gtx)
|
|
default:
|
|
return hint.Layout(gtx)
|
|
}
|
|
})
|
|
frameEvent.Frame(&s.ops)
|
|
return shared.NewState{}
|
|
}
|
|
|
|
func (s *StateEnterTime) handleQuitEvent() shared.NewState {
|
|
return shared.NewState{ExitCode: shared.ErrExitOk}
|
|
}
|
|
|
|
func (s *StateEnterTime) handleTimeEntered(stamp time.Time) shared.NewState {
|
|
// TODO: Switch to countdown state once that is implemented
|
|
return shared.NewState{
|
|
NextState: &StateCountdown{TargetTime: stamp},
|
|
}
|
|
}
|