multiple screen structure
This commit is contained in:
parent
72d2ceab98
commit
3fbab7f9c7
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
@ -45,11 +46,13 @@ 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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
@ -8,13 +15,92 @@ type Task struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type State struct {
|
type State struct {
|
||||||
Status string
|
Status string
|
||||||
Tasks []Task
|
CurrentScreen string
|
||||||
Config map[string]string
|
Tasks []Task
|
||||||
Logs []string
|
Config map[string]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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 = ""
|
||||||
|
|
Loading…
Reference in New Issue