random-apps/timer/windowEnterTime.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},
}
}