Works now, also reorganised code
This commit is contained in:
parent
185c84742a
commit
781f6a10ec
12 changed files with 316 additions and 170 deletions
|
@ -1,7 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import "image/color"
|
|
||||||
|
|
||||||
var (
|
|
||||||
ColorRed = color.NRGBA{R: 0xff, G: 0x0, B: 0x0, A: 255}
|
|
||||||
)
|
|
2
go.mod
2
go.mod
|
@ -5,7 +5,7 @@ go 1.23.5
|
||||||
require (
|
require (
|
||||||
gioui.org v0.8.0 // indirect
|
gioui.org v0.8.0 // indirect
|
||||||
gioui.org/shader v1.0.8 // indirect
|
gioui.org/shader v1.0.8 // indirect
|
||||||
git.mstar.dev/mstar/goutils v1.6.1 // indirect
|
git.mstar.dev/mstar/goutils v1.6.2 // indirect
|
||||||
github.com/go-text/typesetting v0.2.1 // indirect
|
github.com/go-text/typesetting v0.2.1 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -5,6 +5,8 @@ gioui.org/shader v1.0.8 h1:6ks0o/A+b0ne7RzEqRZK5f4Gboz2CfG+mVliciy6+qA=
|
||||||
gioui.org/shader v1.0.8/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM=
|
gioui.org/shader v1.0.8/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM=
|
||||||
git.mstar.dev/mstar/goutils v1.6.1 h1:2yr9GYN8CJByZsJRu1pZ6WBp51Nn+3zJq49ky54xYDk=
|
git.mstar.dev/mstar/goutils v1.6.1 h1:2yr9GYN8CJByZsJRu1pZ6WBp51Nn+3zJq49ky54xYDk=
|
||||||
git.mstar.dev/mstar/goutils v1.6.1/go.mod h1:juxY0eZEMnA95fedRp2LVXvUBgEjz66nE8SEdGKcxMA=
|
git.mstar.dev/mstar/goutils v1.6.1/go.mod h1:juxY0eZEMnA95fedRp2LVXvUBgEjz66nE8SEdGKcxMA=
|
||||||
|
git.mstar.dev/mstar/goutils v1.6.2 h1:yqpEXQJWWiNZaJ2aenG5iCtjNP/gEnX9h5Ui4hrQ1tw=
|
||||||
|
git.mstar.dev/mstar/goutils v1.6.2/go.mod h1:juxY0eZEMnA95fedRp2LVXvUBgEjz66nE8SEdGKcxMA=
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
github.com/go-text/typesetting v0.2.1 h1:x0jMOGyO3d1qFAPI0j4GSsh7M0Q3Ypjzr4+CEVg82V8=
|
github.com/go-text/typesetting v0.2.1 h1:x0jMOGyO3d1qFAPI0j4GSsh7M0Q3Ypjzr4+CEVg82V8=
|
||||||
github.com/go-text/typesetting v0.2.1/go.mod h1:mTOxEwasOFpAMBjEQDhdWRckoLLeI/+qrQeBCTGEt6M=
|
github.com/go-text/typesetting v0.2.1/go.mod h1:mTOxEwasOFpAMBjEQDhdWRckoLLeI/+qrQeBCTGEt6M=
|
||||||
|
|
42
main.go
42
main.go
|
@ -2,32 +2,40 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"sync"
|
||||||
|
|
||||||
"gioui.org/app"
|
"gioui.org/app"
|
||||||
|
"git.mstar.dev/mstar/goutils/other"
|
||||||
|
"git.mstar.dev/mstar/timer/shared"
|
||||||
|
"git.mstar.dev/mstar/timer/timer"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
var t time.Time
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
window := new(app.Window)
|
other.SetupFlags()
|
||||||
var err error
|
other.ConfigureLoggingFromCliArgs()
|
||||||
t, err = time.Parse(time.RFC3339, "2025-01-31T22:50:00Z")
|
// Create window struct outside. Doesn't cause any renders yet
|
||||||
if err != nil {
|
windows := []*app.Window{
|
||||||
log.Fatal().Err(err).Send()
|
new(app.Window),
|
||||||
|
}
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for _, window := range windows {
|
||||||
|
wg.Add(1)
|
||||||
|
go windowLauncher(window, &wg)
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
// window.Option(func(m unit.Metric, c *app.Config) {
|
wg.Wait()
|
||||||
// c.Size = image.Pt(500, 150)
|
log.Info().Msg("All windows closed, exiting")
|
||||||
// c.MaxSize = image.Pt(500, 150)
|
|
||||||
// c.MinSize = image.Pt(500, 150)
|
|
||||||
// })
|
|
||||||
err := run(window, &StateEnterTime{})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Failed to run main window")
|
|
||||||
}
|
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}()
|
}()
|
||||||
app.Main()
|
app.Main()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func windowLauncher(window *app.Window, wg *sync.WaitGroup) {
|
||||||
|
defer wg.Done()
|
||||||
|
log.Info().Msg("Starting window")
|
||||||
|
err := shared.Run(window, &timer.StateEnterTime{})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("Failed to run main window")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
14
shared/constants.go
Normal file
14
shared/constants.go
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package shared
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"image/color"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ColorRed = color.NRGBA{R: 0xff, G: 0x0, B: 0x0, A: 255}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrExitOk = errors.New("exit ok")
|
||||||
|
)
|
6
shared/theming.go
Normal file
6
shared/theming.go
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
package shared
|
||||||
|
|
||||||
|
import "image/color"
|
||||||
|
|
||||||
|
var BackgroundColor = color.NRGBA{R: 0x0, G: 0x0, B: 0x0, A: 0xff}
|
||||||
|
var TextColor = color.NRGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}
|
46
shared/window.go
Normal file
46
shared/window.go
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
package shared
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"gioui.org/app"
|
||||||
|
"gioui.org/widget/material"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GlobalState struct {
|
||||||
|
Window *app.Window
|
||||||
|
Theme *material.Theme
|
||||||
|
state WindowState
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run(window *app.Window, initialState WindowState) error {
|
||||||
|
// Set up global data first
|
||||||
|
state := GlobalState{}
|
||||||
|
theme := material.NewTheme()
|
||||||
|
theme.Bg = BackgroundColor
|
||||||
|
theme.Fg = TextColor
|
||||||
|
state.Theme = theme
|
||||||
|
state.Window = window
|
||||||
|
state.state = initialState
|
||||||
|
// Then start showing the window and polling for events
|
||||||
|
// Though technically, I don't think window.Event will cause the window to display yet
|
||||||
|
// since what should it display? It doesn't have anything to display. So first display happens at event.Frame
|
||||||
|
for {
|
||||||
|
e := window.Event()
|
||||||
|
// Let the current view handle the event, then update accordingly
|
||||||
|
newState := state.state.Run(&state, e)
|
||||||
|
if newState.NextState != nil {
|
||||||
|
state.state = newState.NextState
|
||||||
|
}
|
||||||
|
if newState.ExitCode != nil {
|
||||||
|
log.Info().Msg("Received shutdown request")
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(1)
|
||||||
|
go state.state.Exit(&state, &wg)
|
||||||
|
wg.Wait()
|
||||||
|
log.Info().Msg("Shutdown of window complete")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,18 +1,21 @@
|
||||||
package main
|
package shared
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
"gioui.org/io/event"
|
"gioui.org/io/event"
|
||||||
)
|
)
|
||||||
|
|
||||||
type WindowState interface {
|
type WindowState interface {
|
||||||
Run(globalState *GlobalState, event event.Event) NewState
|
Run(globalState *GlobalState, event event.Event) NewState
|
||||||
|
Exit(globalState *GlobalState, wg *sync.WaitGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
type NewState struct {
|
type NewState struct {
|
||||||
// If null, keep current state
|
// If null, keep current state
|
||||||
NextState WindowState
|
NextState WindowState
|
||||||
Error error
|
Error error
|
||||||
ExitCode *int
|
ExitCode error
|
||||||
}
|
}
|
||||||
|
|
||||||
func EmptyEvent() NewState {
|
func EmptyEvent() NewState {
|
88
timer/windowCountdown.go
Normal file
88
timer/windowCountdown.go
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
package timer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gioui.org/app"
|
||||||
|
"gioui.org/io/event"
|
||||||
|
"gioui.org/op"
|
||||||
|
"gioui.org/op/clip"
|
||||||
|
"gioui.org/op/paint"
|
||||||
|
"gioui.org/text"
|
||||||
|
"gioui.org/widget/material"
|
||||||
|
"git.mstar.dev/mstar/timer/shared"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StateCountdown struct {
|
||||||
|
initialSetupDone bool
|
||||||
|
TargetTime time.Time
|
||||||
|
ops op.Ops
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateCountdown) 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 *StateCountdown) Exit(globalState *shared.GlobalState, wg *sync.WaitGroup) {
|
||||||
|
wg.Done()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateCountdown) initialSetup(globalState *shared.GlobalState) {
|
||||||
|
s.initialSetupDone = true
|
||||||
|
go func(window *app.Window) {
|
||||||
|
t := time.NewTicker(time.Second)
|
||||||
|
for range t.C {
|
||||||
|
window.Invalidate()
|
||||||
|
}
|
||||||
|
}(globalState.Window)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateCountdown) handleQuitEvent() shared.NewState {
|
||||||
|
return shared.NewState{ExitCode: shared.ErrExitOk}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateCountdown) handleFrameEvent(
|
||||||
|
globalState *shared.GlobalState,
|
||||||
|
frameEvent *app.FrameEvent,
|
||||||
|
) shared.NewState {
|
||||||
|
gtx := app.NewContext(&s.ops, *frameEvent)
|
||||||
|
paint.FillShape(
|
||||||
|
gtx.Ops,
|
||||||
|
globalState.Theme.Bg,
|
||||||
|
clip.Rect(image.Rect(0, 0, gtx.Constraints.Max.X, gtx.Constraints.Max.Y)).Op(),
|
||||||
|
)
|
||||||
|
diff := frameEvent.Now.Sub(s.TargetTime)
|
||||||
|
diff *= -1
|
||||||
|
title := material.H1(
|
||||||
|
globalState.Theme,
|
||||||
|
diff.Round(time.Second).String(),
|
||||||
|
)
|
||||||
|
// //
|
||||||
|
// // // Change the color of the label.
|
||||||
|
title.Color = shared.TextColor
|
||||||
|
// //
|
||||||
|
// // // Change the position of the label.
|
||||||
|
title.Alignment = text.Middle
|
||||||
|
//
|
||||||
|
// // Draw the label to the graphics context.
|
||||||
|
title.Layout(gtx)
|
||||||
|
// // Pass the drawing operations to the GPU.
|
||||||
|
frameEvent.Frame(&s.ops)
|
||||||
|
return shared.NewState{}
|
||||||
|
}
|
129
timer/windowEnterTime.go
Normal file
129
timer/windowEnterTime.go
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
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},
|
||||||
|
}
|
||||||
|
}
|
62
window.go
62
window.go
|
@ -1,62 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"image/color"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"gioui.org/app"
|
|
||||||
"gioui.org/widget/material"
|
|
||||||
)
|
|
||||||
|
|
||||||
type GlobalState struct {
|
|
||||||
Window *app.Window
|
|
||||||
Theme *material.Theme
|
|
||||||
state WindowState
|
|
||||||
}
|
|
||||||
|
|
||||||
func ticker(window *app.Window) {
|
|
||||||
t := time.NewTicker(time.Second)
|
|
||||||
for range t.C {
|
|
||||||
window.Invalidate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func run(window *app.Window, initialState WindowState) error {
|
|
||||||
state := GlobalState{}
|
|
||||||
theme := material.NewTheme()
|
|
||||||
theme.Bg = color.NRGBA{A: 0xff}
|
|
||||||
state.Theme = theme
|
|
||||||
state.Window = window
|
|
||||||
state.state = initialState
|
|
||||||
for {
|
|
||||||
e := window.Event()
|
|
||||||
newState := state.state.Run(&state, e)
|
|
||||||
if newState.NextState != nil {
|
|
||||||
state.state = newState.NextState
|
|
||||||
}
|
|
||||||
if newState.ExitCode != nil {
|
|
||||||
os.Exit(*newState.ExitCode)
|
|
||||||
}
|
|
||||||
// log.Debug().Any("size", e.Size).Msg("Redrawing")
|
|
||||||
// // This graphics context is used for managing the rendering state.
|
|
||||||
// gtx := app.NewContext(&ops, e)
|
|
||||||
// paint.FillShape(gtx.Ops, theme.Bg, clip.Rect(image.Rect(0, 0, gtx.Constraints.Max.X, gtx.Constraints.Max.Y)).Op())
|
|
||||||
// //
|
|
||||||
// // // Define an large label with an appropriate text:
|
|
||||||
// log.Debug().Dur("d", t.Sub(e.Now)).Time("t", t).Send()
|
|
||||||
// title := material.H1(theme, e.Now.Sub(t).Round(time.Second).String())
|
|
||||||
// //
|
|
||||||
// // // Change the color of the label.
|
|
||||||
// maroon := color.NRGBA{R: 0xff, G: 0xff, B: 0xff, A: 255}
|
|
||||||
// title.Color = maroon
|
|
||||||
// //
|
|
||||||
// // // Change the position of the label.
|
|
||||||
// title.Alignment = text.Middle
|
|
||||||
//
|
|
||||||
// // Draw the label to the graphics context.
|
|
||||||
// title.Layout(gtx)
|
|
||||||
// // Pass the drawing operations to the GPU.
|
|
||||||
// e.Frame(&ops)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"gioui.org/app"
|
|
||||||
"gioui.org/io/event"
|
|
||||||
"gioui.org/layout"
|
|
||||||
"gioui.org/op"
|
|
||||||
"gioui.org/widget"
|
|
||||||
"gioui.org/widget/material"
|
|
||||||
"git.mstar.dev/mstar/goutils/other"
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
type StateEnterTime struct {
|
|
||||||
initialSetupDone bool
|
|
||||||
editor *widget.Editor
|
|
||||||
inputField *material.EditorStyle
|
|
||||||
ops op.Ops
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StateEnterTime) initialSetup(globalState *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
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StateEnterTime) Run(globalState *GlobalState, event event.Event) 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 EmptyEvent()
|
|
||||||
default:
|
|
||||||
log.Debug().Any("event", event).Type("type", event).Msg("Unknown event")
|
|
||||||
return NewState{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StateEnterTime) handleFrameEvent(
|
|
||||||
globalState *GlobalState,
|
|
||||||
frameEvent *app.FrameEvent,
|
|
||||||
) NewState {
|
|
||||||
gtx := app.NewContext(&s.ops, *frameEvent)
|
|
||||||
for ev, ok := s.editor.Update(gtx); ok; ev, ok = s.editor.Update(gtx) {
|
|
||||||
log.Debug().Any("Editor event", ev).Send()
|
|
||||||
}
|
|
||||||
label := material.H3(globalState.Theme, "Enter timestamp")
|
|
||||||
hint := material.Subtitle1(
|
|
||||||
globalState.Theme,
|
|
||||||
"Format: <YYYY>-<MM>-<DD>T<hh>:<mm>:<ss>(Z|+<TZ>)",
|
|
||||||
)
|
|
||||||
list := layout.List{Axis: layout.Vertical}
|
|
||||||
list.Layout(gtx, 3, 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)
|
|
||||||
default:
|
|
||||||
return hint.Layout(gtx)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
frameEvent.Frame(&s.ops)
|
|
||||||
return NewState{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StateEnterTime) handleQuitEvent() NewState {
|
|
||||||
return NewState{ExitCode: other.IntoPointer(0)}
|
|
||||||
}
|
|
Loading…
Reference in a new issue