Designing task list
我们在这一节中将会设计一个,左边是列表,右边列表中代办事项的具体内容
实现效果如图:
实现该需要使用总共用了两个文件,一个task_list.go
package main
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"time"
)
type taskApp struct {
data *taskList
// 记录当前代办事项
visible []*task
// 当前选中的task
current *task
tasks *widget.List
// Entry widget allows simple text to be input when focused.
title, description, due *widget.Entry
category *widget.Select
priority *widget.RadioGroup
completion *widget.Slider
}
func (a *taskApp) refreshData() {
// hide done
a.visible = a.data.remaining()
a.tasks.Refresh()
}
func (a *taskApp) setTask(t *task) {
// 最新设置的那个task就是当前的代办列表
a.current = t
// 将data的title设置到APP的title中
a.title.SetText(t.title)
a.description.SetText(t.description)
a.category.SetSelected(t.category)
if t.priority == midPriority {
a.priority.SetSelected("Mid")
} else if t.priority == highPriority {
a.priority.SetSelected("High")
} else {
a.priority.SetSelected("Low")
}
a.due.SetText(formatData(t.due))
a.completion.Value = t.completion
a.completion.Refresh()
}
func formatData(date *time.Time) string {
if date == nil {
return ""
}
return date.Format(dateFormat)
}
func (a *taskApp) makeUI() fyne.CanvasObject {
a.tasks = widget.NewList(
func() int {
return len(a.visible)
},
func() fyne.CanvasObject {
return container.NewHBox(widget.NewCheck("", func(bool) {}),
widget.NewLabel("TODO ITtem x"))
},
func(i widget.ListItemID, c fyne.CanvasObject) {
task := a.visible[i]
box := c.(*fyne.Container)
check := box.Objects[0].(*widget.Check)
check.Checked = task.done
check.OnChanged = func(done bool) {
task.done = done
a.refreshData()
}
labelData := box.Objects[1].(*widget.Label)
labelData.SetText(task.title)
})
// 当选中一个list的时候,调用该回调函数
// 选中哪个list之后,将界面上需要显示的都更换成当前ID 的list对象
a.tasks.OnSelected = func(id widget.ListItemID) {
a.setTask(a.visible[id])
}
// 标题支持输入
a.title = widget.NewEntry()
a.title.OnChanged = func(text string) {
if a.current == nil {
return
}
// 更新当前选中的标题
a.current.title = text
a.tasks.Refresh()
}
a.description = widget.NewMultiLineEntry()
a.description.OnChanged = func(text string) {
if a.current == nil {
return
}
a.current.description = text
}
a.category = widget.NewSelect([]string{"Home"}, func(cat string) {
if a.current == nil {
return
}
a.current.category = cat
})
a.priority = widget.NewRadioGroup([]string{"Low", "Mid", "High"}, func(pri string) {
if a.current == nil {
return
}
if pri == "Mid" {
a.current.priority = midPriority
} else if pri == "High" {
a.current.priority = highPriority
} else {
a.current.priority = lowPriority
}
})
a.due = widget.NewEntry()
a.due.Validator = dateValidator
a.due.OnChanged = func(str string) {
if a.current == nil {
return
}
if str == "" {
a.current.due = nil
} else {
date, err := time.Parse(dateFormat, str)
if err != nil {
a.current.due = &date
}
}
}
a.completion = widget.NewSlider(0, 100)
a.completion.OnChanged = func(done float64) {
if a.current == nil {
return
}
a.current.completion = done
}
details := widget.NewForm(
widget.NewFormItem("Title", a.title),
widget.NewFormItem("Description", a.description),
widget.NewFormItem("Category", a.category),
widget.NewFormItem("Priority", a.priority),
widget.NewFormItem("Due", a.due),
widget.NewFormItem("Completion", a.completion),
)
toolBar := widget.NewToolbar(
widget.NewToolbarAction(theme.ContentAddIcon(), func() {
task := &task{title: "New task"}
a.data.add(task)
a.setTask(task)
a.refreshData()
}),
)
return container.NewBorder(toolBar, nil, a.tasks, nil, details)
}
/**
* 创建一个代办事项的APP
*/
func main() {
a := app.New()
w := a.NewWindow("Task List")
data := dummyData()
tasks := &taskApp{data: data, visible: data.remaining()}
w.SetContent(tasks.makeUI())
// 若是首次启动的时候,项目大于0就显示首个list
if len(data.remaining()) > 0 {
tasks.setTask(data.remaining()[0])
}
w.ShowAndRun()
}
还有一个是data.go
package main
import "time"
const (
dateFormat = "02 Jan 06 15:04"
lowPriority = 0
midPriority = 1
highPriority = 2
)
func dateValidator(text string) error {
_,err := time.Parse(dateFormat, text)
return err
}
type task struct {
// 定义标题和描述语句
title, description string
// 该任务是否已经完成
done bool
category string
priority int
due *time.Time
completion float64
}
// 定义任务链表的切片
type taskList struct {
tasks []*task
}
// 添加任务,新添加的任务放到列表头
func (l *taskList) add(t *task) {
// 使用t初始化一个一样的切片,并将原有的切片添加到后面
l.tasks = append([]*task{t}, l.tasks...)
}
/* 获取剩余没有完成的任务列表 */
func (l *taskList) remaining() []*task {
var items []*task
for _, task := range l.tasks {
if !task.done {
items = append(items, task)
}
}
return items
}
/* 获取已经完成的任务列表 */
func (l *taskList) done() []*task {
var items []*task
for _, task := range l.tasks {
if task.done {
items = append(items, task)
}
}
return items
}
func dummyData() *taskList {
return &taskList{
tasks: []*task{
{title: "Nearly done", description: "you can tick my checkbox and I will be marked as done and disappear"},
{title: "Functions", description: "Tap the plus icon above to add a new task, or tap the munus icon to remove this one"},
},
}
}
下面会讲解下具体思路
如图,我们首先需要创建一个toolbar,需要使用到函数NewToolbar,toolbar里面需要放置一个添加按钮
toolBar := widget.NewToolbar(
widget.NewToolbarAction(theme.ContentAddIcon(), func() {
task := &task{title: "New task"}
a.data.add(task)
a.setTask(task)
a.refreshData()
}),
)
然后添加面板中各个入口的对象
// 当选中一个list的时候,调用该回调函数
// 选中哪个list之后,将界面上需要显示的都更换成当前ID 的list对象
a.tasks.OnSelected = func(id widget.ListItemID) {
a.setTask(a.visible[id])
}
// 标题支持输入
a.title = widget.NewEntry()
a.title.OnChanged = func(text string) {
if a.current == nil {
return
}
// 更新当前选中的标题
a.current.title = text
a.tasks.Refresh()
}
a.description = widget.NewMultiLineEntry()
a.description.OnChanged = func(text string) {
if a.current == nil {
return
}
a.current.description = text
}
a.category = widget.NewSelect([]string{"Home"}, func(cat string) {
if a.current == nil {
return
}
a.current.category = cat
})
a.priority = widget.NewRadioGroup([]string{"Low", "Mid", "High"}, func(pri string) {
if a.current == nil {
return
}
if pri == "Mid" {
a.current.priority = midPriority
} else if pri == "High" {
a.current.priority = highPriority
} else {
a.current.priority = lowPriority
}
})
a.due = widget.NewEntry()
a.due.Validator = dateValidator
a.due.OnChanged = func(str string) {
if a.current == nil {
return
}
if str == "" {
a.current.due = nil
} else {
date, err := time.Parse(dateFormat, str)
if err != nil {
a.current.due = &date
}
}
}
a.completion = widget.NewSlider(0, 100)
a.completion.OnChanged = func(done float64) {
if a.current == nil {
return
}
a.current.completion = done
}
details := widget.NewForm(
widget.NewFormItem("Title", a.title),
widget.NewFormItem("Description", a.description),
widget.NewFormItem("Category", a.category),
widget.NewFormItem("Priority", a.priority),
widget.NewFormItem("Due", a.due),
widget.NewFormItem("Completion", a.completion),
)
toolBar := widget.NewToolbar(
widget.NewToolbarAction(theme.ContentAddIcon(), func() {
task := &task{title: "New task"}
a.data.add(task)
a.setTask(task)
a.refreshData()
}),
)