multiple screen structure

This commit is contained in:
Erik Winter 2022-10-24 15:57:55 +02:00
parent 72d2ceab98
commit 3fbab7f9c7
6 changed files with 194 additions and 58 deletions

View File

@ -9,7 +9,7 @@ import (
func main() { func main() {
fyneApp := app.NewWithID("nl.ewintr.gte") fyneApp := app.NewWithID("nl.ewintr.gte")
w := fyneApp.NewWindow("gte - getting things email") fyneWindow := fyneApp.NewWindow("gte - getting things email")
conf := component.NewConfigurationFromPreferences(fyneApp.Preferences()) conf := component.NewConfigurationFromPreferences(fyneApp.Preferences())
conf.Load() conf.Load()
logger := component.NewLogger() logger := component.NewLogger()
@ -20,9 +20,9 @@ func main() {
} }
r := runner.NewRunner(conf, tasksURI, logger) r := runner.NewRunner(conf, tasksURI, logger)
tabs := r.Init() rootContainer := r.Init()
w.SetContent(tabs) fyneWindow.SetContent(rootContainer)
go r.Run() go r.Run()
w.ShowAndRun() fyneWindow.ShowAndRun()
} }

View File

@ -11,7 +11,6 @@ import (
"ewintr.nl/gte/cmd/android-app/screen" "ewintr.nl/gte/cmd/android-app/screen"
"ewintr.nl/gte/internal/task" "ewintr.nl/gte/internal/task"
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/storage" "fyne.io/fyne/v2/storage"
) )
@ -23,11 +22,11 @@ type Runner struct {
conf *component.Configuration conf *component.Configuration
logger *component.Logger logger *component.Logger
tasks *component.Tasks tasks *component.Tasks
screens []screen.Screen
status string status string
requests chan interface{} requests chan interface{}
refresh chan bool refresh chan bool
fileURI fyne.URI fileURI fyne.URI
screenSet *screen.ScreenSet
} }
func NewRunner(conf *component.Configuration, fileURI fyne.URI, logger *component.Logger) *Runner { func NewRunner(conf *component.Configuration, fileURI fyne.URI, logger *component.Logger) *Runner {
@ -42,30 +41,20 @@ func NewRunner(conf *component.Configuration, fileURI fyne.URI, logger *componen
} }
func (r *Runner) Init() fyne.CanvasObject { func (r *Runner) Init() fyne.CanvasObject {
logScreen := screen.NewLog()
logTab := container.NewTabItem("log", logScreen.Content())
r.screens = append(r.screens, logScreen)
configScreen := screen.NewConfig(r.requests)
configTab := container.NewTabItem("config", configScreen.Content())
r.screens = append(r.screens, configScreen)
stored, err := load(r.fileURI) stored, err := load(r.fileURI)
if err != nil { if err != nil {
r.logger.Log(err.Error()) r.logger.Log(err.Error())
} }
r.logger.Log(fmt.Sprintf("loaded %d tasks from file", len(stored))) r.logger.Log(fmt.Sprintf("loaded %d tasks from file", len(stored)))
r.tasks = component.NewTasks(r.conf, stored) r.tasks = component.NewTasks(r.conf, stored)
taskScreen := screen.NewTasks(r.requests)
taskTab := container.NewTabItem("tasks", taskScreen.Content())
r.screens = append(r.screens, taskScreen)
tabs := container.NewAppTabs(taskTab, configTab, logTab)
return tabs r.screenSet = screen.NewScreenSet(r.requests)
return r.screenSet.Content()
} }
func (r *Runner) Run() { func (r *Runner) Run() {
go r.screenSet.Run()
go r.refresher() go r.refresher()
go r.processRequest() go r.processRequest()
r.backgroundSync() r.backgroundSync()
@ -80,7 +69,7 @@ func (r *Runner) processRequest() {
for k, val := range v.Fields { for k, val := range v.Fields {
r.conf.Set(k, val) r.conf.Set(k, val)
} }
r.status = "ready" r.status = "config saved"
r.logger.Log("config saved") r.logger.Log("config saved")
case screen.SyncTasksRequest: case screen.SyncTasksRequest:
r.status = "syncing..." r.status = "syncing..."
@ -92,7 +81,7 @@ func (r *Runner) processRequest() {
if countDisp > 0 || countFetch > 0 { if countDisp > 0 || countFetch > 0 {
r.logger.Log(fmt.Sprintf("task sync: dispatched: %d, fetched: %d", countDisp, countFetch)) r.logger.Log(fmt.Sprintf("task sync: dispatched: %d, fetched: %d", countDisp, countFetch))
} }
r.status = "ready" r.status = "synced"
all, err := r.tasks.All() all, err := r.tasks.All()
if err != nil { if err != nil {
r.logger.Log(err.Error()) r.logger.Log(err.Error())
@ -103,10 +92,13 @@ func (r *Runner) processRequest() {
break break
} }
case screen.MarkTaskDoneRequest: case screen.MarkTaskDoneRequest:
r.status = "marking done..."
r.refresh <- true
if err := r.tasks.MarkDone(v.ID); err != nil { if err := r.tasks.MarkDone(v.ID); err != nil {
r.logger.Log(err.Error()) r.logger.Log(err.Error())
} }
r.logger.Log(fmt.Sprintf("marked task %q done", v.ID)) r.logger.Log(fmt.Sprintf("marked task %q done", v.ID))
r.status = "marked done"
default: default:
r.logger.Log("request unknown") r.logger.Log("request unknown")
} }
@ -135,9 +127,7 @@ func (r *Runner) refresher() {
Logs: r.logger.Lines(), Logs: r.logger.Lines(),
} }
for _, s := range r.screens { r.screenSet.Refresh(state)
s.Refresh(state)
}
} }
} }

View File

@ -4,6 +4,7 @@ import (
"sync" "sync"
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/data/binding" "fyne.io/fyne/v2/data/binding"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
) )
@ -46,10 +47,12 @@ func (ff *FormField) GetValue() string {
type Config struct { type Config struct {
fields []*FormField fields []*FormField
out chan interface{} commands chan interface{}
show chan string
root *fyne.Container
} }
func NewConfig(out chan interface{}) *Config { func NewConfig(commands chan interface{}, show chan string) *Config {
fields := []*FormField{} fields := []*FormField{}
for _, f := range [][2]string{ for _, f := range [][2]string{
{"ConfigIMAPURL", "imap url"}, {"ConfigIMAPURL", "imap url"},
@ -68,10 +71,14 @@ func NewConfig(out chan interface{}) *Config {
fields = append(fields, NewFormField(f[0], f[1])) fields = append(fields, NewFormField(f[0], f[1]))
} }
return &Config{ config := &Config{
fields: fields, fields: fields,
out: out, commands: commands,
show: show,
} }
config.Init()
return config
} }
func (cf *Config) Save() { func (cf *Config) Save() {
@ -81,7 +88,8 @@ func (cf *Config) Save() {
for _, f := range cf.fields { for _, f := range cf.fields {
req.Fields[f.Key] = f.GetValue() req.Fields[f.Key] = f.GetValue()
} }
cf.out <- req cf.commands <- req
cf.show <- "tasks"
} }
func (cf *Config) Refresh(state State) { func (cf *Config) Refresh(state State) {
@ -92,7 +100,7 @@ func (cf *Config) Refresh(state State) {
} }
} }
func (cf *Config) Content() fyne.CanvasObject { func (cf *Config) Init() {
configForm := widget.NewForm() configForm := widget.NewForm()
for _, f := range cf.fields { for _, f := range cf.fields {
w := widget.NewEntry() w := widget.NewEntry()
@ -104,5 +112,23 @@ func (cf *Config) Content() fyne.CanvasObject {
configForm.OnSubmit = cf.Save configForm.OnSubmit = cf.Save
configForm.Enable() configForm.Enable()
return configForm cf.root = container.NewBorder(
nil,
nil,
nil,
nil,
configForm,
)
}
func (cf *Config) Content() *fyne.Container {
return cf.root
}
func (cf *Config) Hide() {
cf.root.Hide()
}
func (cf *Config) Show() {
cf.root.Show()
} }

View File

@ -2,25 +2,30 @@ package screen
import ( import (
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/data/binding" "fyne.io/fyne/v2/data/binding"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
) )
type Log struct { type Log struct {
lines binding.StringList lines binding.StringList
root *fyne.Container
} }
func NewLog() *Log { func NewLog() *Log {
return &Log{ logs := &Log{
lines: binding.NewStringList(), lines: binding.NewStringList(),
} }
logs.Init()
return logs
} }
func (l *Log) Refresh(state State) { func (l *Log) Refresh(state State) {
l.lines.Set(state.Logs) l.lines.Set(state.Logs)
} }
func (l *Log) Content() fyne.CanvasObject { func (l *Log) Init() {
list := widget.NewListWithData( list := widget.NewListWithData(
l.lines, l.lines,
func() fyne.CanvasObject { func() fyne.CanvasObject {
@ -30,5 +35,23 @@ func (l *Log) Content() fyne.CanvasObject {
o.(*widget.Label).Bind(i.(binding.String)) o.(*widget.Label).Bind(i.(binding.String))
}, },
) )
return list l.root = container.NewBorder(
nil,
nil,
nil,
nil,
list,
)
}
func (l *Log) Content() *fyne.Container {
return l.root
}
func (l *Log) Hide() {
l.root.Hide()
}
func (l *Log) Show() {
l.root.Show()
} }

View File

@ -1,6 +1,13 @@
package screen package screen
import "fyne.io/fyne/v2" import (
"fmt"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/data/binding"
"fyne.io/fyne/v2/widget"
)
type Task struct { type Task struct {
ID string ID string
@ -9,12 +16,91 @@ type Task struct {
type State struct { type State struct {
Status string Status string
CurrentScreen string
Tasks []Task Tasks []Task
Config map[string]string Config map[string]string
Logs []string Logs []string
} }
type Screen interface { type Screen interface {
Content() fyne.CanvasObject Content() *fyne.Container
Refresh(state State) Refresh(state State)
Hide()
Show()
}
type ScreenSet struct {
current string
show chan string
status binding.String
menu *fyne.Container
screens map[string]Screen
root *fyne.Container
}
func NewScreenSet(requests chan interface{}) *ScreenSet {
status := binding.NewString()
show := make(chan string)
tasksButton := widget.NewButton("tasks", func() {
show <- "tasks"
})
configButton := widget.NewButton("config", func() {
show <- "config"
})
logsButton := widget.NewButton("logs", func() {
show <- "logs"
})
statusLabel := widget.NewLabel("> init...")
statusLabel.Bind(status)
statusLabel.TextStyle.Italic = true
menu := container.NewHBox(tasksButton, configButton, logsButton, statusLabel)
screens := map[string]Screen{
"tasks": NewTasks(requests, show),
"logs": NewLog(),
"config": NewConfig(requests, show),
}
cs := []fyne.CanvasObject{}
for _, s := range screens {
s.Hide()
cs = append(cs, s.Content())
}
screens["tasks"].Show()
root := container.NewBorder(menu, nil, nil, nil, cs...)
return &ScreenSet{
status: status,
current: "tasks",
show: show,
screens: screens,
root: root,
}
}
func (ss *ScreenSet) Run() {
for s := range ss.show {
if s != ss.current {
ss.screens[ss.current].Hide()
ss.screens[s].Show()
ss.current = s
ss.root.Refresh()
}
}
}
func (ss *ScreenSet) Refresh(state State) {
ss.status.Set(fmt.Sprintf("> %s", state.Status))
for _, s := range ss.screens {
s.Refresh(state)
}
ss.root.Refresh()
}
func (ss *ScreenSet) Content() *fyne.Container {
return ss.root
} }

View File

@ -1,7 +1,6 @@
package screen package screen
import ( import (
"fmt"
"sort" "sort"
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
@ -17,25 +16,28 @@ type MarkTaskDoneRequest struct {
} }
type Tasks struct { type Tasks struct {
status binding.String
tasks []Task tasks []Task
taskLabels binding.StringList taskLabels binding.StringList
selectedTask string selectedTask string
list *widget.List list *widget.List
out chan interface{} commands chan interface{}
show chan string
root *fyne.Container
} }
func NewTasks(out chan interface{}) *Tasks { func NewTasks(commands chan interface{}, show chan string) *Tasks {
return &Tasks{ tasks := &Tasks{
status: binding.NewString(),
tasks: []Task{}, tasks: []Task{},
taskLabels: binding.NewStringList(), taskLabels: binding.NewStringList(),
out: out, commands: commands,
show: show,
} }
tasks.Init()
return tasks
} }
func (t *Tasks) Refresh(state State) { func (t *Tasks) Refresh(state State) {
t.status.Set(fmt.Sprintf("> %s", state.Status))
t.tasks = state.Tasks t.tasks = state.Tasks
sort.Slice(t.tasks, func(i, j int) bool { sort.Slice(t.tasks, func(i, j int) bool {
return t.tasks[i].Action < t.tasks[j].Action return t.tasks[i].Action < t.tasks[j].Action
@ -50,10 +52,7 @@ func (t *Tasks) Refresh(state State) {
} }
} }
func (t *Tasks) Content() fyne.CanvasObject { func (t *Tasks) Init() {
statusLabel := widget.NewLabel("> init...")
statusLabel.Bind(t.status)
statusLabel.TextStyle.Italic = true
doneButton := widget.NewButton("done", func() { doneButton := widget.NewButton("done", func() {
t.markDone() t.markDone()
}) })
@ -68,8 +67,8 @@ func (t *Tasks) Content() fyne.CanvasObject {
) )
t.list.OnSelected = t.selectItem t.list.OnSelected = t.selectItem
return container.NewBorder( t.root = container.NewBorder(
container.NewHBox(statusLabel), nil,
doneButton, doneButton,
nil, nil,
nil, nil,
@ -77,6 +76,18 @@ func (t *Tasks) Content() fyne.CanvasObject {
) )
} }
func (t *Tasks) Content() *fyne.Container {
return t.root
}
func (t *Tasks) Hide() {
t.root.Hide()
}
func (t *Tasks) Show() {
t.root.Show()
}
func (t *Tasks) selectItem(lid widget.ListItemID) { func (t *Tasks) selectItem(lid widget.ListItemID) {
id := int(lid) id := int(lid)
if id < 0 || id >= len(t.tasks) { if id < 0 || id >= len(t.tasks) {
@ -90,7 +101,7 @@ func (t *Tasks) markDone() {
if t.selectedTask == "" { if t.selectedTask == "" {
return return
} }
t.out <- MarkTaskDoneRequest{ t.commands <- MarkTaskDoneRequest{
ID: t.selectedTask, ID: t.selectedTask,
} }
t.selectedTask = "" t.selectedTask = ""