0

我正在尝试制作一个文件选择小部件(几乎在我负责的每个开发中都需要它)。该小部件由一个标签、一个条目和一个按钮(按此顺序)组成,并且它们都水平显示在同一行上。
我希望避免(如果可能的话)制作一个全新的自定义小部件,因为 100% 的功能已经存在于 sandard fyne 小部件中。所以我的想法是制作一个自定义布局并将我的小部件放在那里。
问题是,由于我的构造函数返回一个 fyne.Container,我无法访问我需要访问的 Entry.Text 字段。
当然,container.Objects[1].(widgets.Entry).Text每次我需要访问它时我都可以这样做,但这似乎不直观。
所以然后我想我会扩展 fyne.Container 以通过一种方法将文本返回给我,但是似乎容器没有类似的东西ExtendBaseWidget()来获得容器的所有功能。
这是我到目前为止的工作代码:

package fyne_custom

import (
    "os"

    "fyne.io/fyne/v2"
    "fyne.io/fyne/v2/container"
    "fyne.io/fyne/v2/widget"
    "github.com/sqweek/dialog"
)

type FileChoiceLayout struct {
    Label, Entry, Button fyne.CanvasObject
}

func (f FileChoiceLayout) MinSize(_ []fyne.CanvasObject) fyne.Size {
    total := fyne.NewSize(0, 0)
    total = total.Add(f.Label.MinSize())
    total = total.Add(f.Entry.MinSize())
    total = total.Add(f.Button.MinSize())
    return total
}

func (f FileChoiceLayout) Layout(items []fyne.CanvasObject, size fyne.Size) {
    topLeft := fyne.NewPos(0, 0)
    for _, item := range items {
        if item == f.Label {
            item.Move(topLeft)
            item.Resize(item.MinSize())
            topLeft = topLeft.Add(fyne.NewPos(item.MinSize().Width, 0))
        } else if item == f.Entry {
            item.Move(topLeft)
            width := size.Width - f.Label.MinSize().Width - f.Button.MinSize().Width
            item.Resize(fyne.NewSize(width, item.MinSize().Height))
            topLeft = topLeft.Add(fyne.NewPos(width, 0))
        } else if item == f.Button {
            item.Move(topLeft)
            item.Resize(item.MinSize())
        }
    }
}

func NewFileChoice(labelText, placeHolder, buttonText string) (*fyne.Container, *widget.Entry) {
    label := widget.NewLabel(labelText)
    entry := widget.NewEntry()
    entry.PlaceHolder = placeHolder
    button := widget.NewButton(buttonText, func() {
        cwd, _ := os.Getwd()
        file, err := dialog.File().Load()
        if err != nil {
            file = ""
        }
        if file != "" {
            entry.SetText(file)
        }
        os.Chdir(cwd)
    })
    container := container.New(&FileChoiceLayout{Label: label, Entry: entry, Button: button}, label, entry, button)
    return container, entry
}

到目前为止,我发现的临时解决方法是不仅返回容器,还返回条目小部件以访问其文本字段。
我在想一定有更好的方法来做到这一点,但我似乎找不到。任何想法将不胜感激!

4

1 回答 1

0

我最终制作了自定义小部件。
最后没有那么多代码,它按我的意图工作。唯一的问题是我不确定我做对了。我的 Destroy() 和 Update() 方法是空的。我不确定这是否应该是这样。而且我必须使用绑定字符串来让数据在小部件结构和它的渲染器之间同步。
如果有更了解的人有意见,我完全愿意接受!
无论如何,如果有人需要这个小部件,这里是代码:

package fyne_custom

import (
    "os"

    "fyne.io/fyne/v2"
    "fyne.io/fyne/v2/data/binding"
    "fyne.io/fyne/v2/widget"
    "github.com/sqweek/dialog"
)

type FileChoice struct {
    widget.BaseWidget
    path                               binding.String
    labelText, placeHolder, buttonText string
}

func (fc FileChoice) Text() string {
    text, _ := fc.path.Get()
    return text
}

func NewFileChoice(labelText, placeHolder, buttonText string) *FileChoice {
    f := &FileChoice{}
    f.path = binding.NewString()
    f.labelText = labelText
    f.placeHolder = placeHolder
    f.buttonText = buttonText
    f.ExtendBaseWidget(f)
    return f
}

type fileChoiceRender struct {
    fc     *FileChoice
    label  *widget.Label
    entry  *widget.Entry
    button *widget.Button
}

func (fcr fileChoiceRender) Objects() []fyne.CanvasObject {
    return []fyne.CanvasObject{fcr.label, fcr.entry, fcr.button}
}

func (fcr fileChoiceRender) MinSize() fyne.Size {
    var minHeight float32
    var totalWidth float32
    for _, item := range fcr.Objects() {
        if item.MinSize().Height > minHeight {
            minHeight = item.MinSize().Height
        }
        totalWidth += item.MinSize().Width
    }
    return fyne.NewSize(totalWidth, minHeight)
}

func (fcr fileChoiceRender) Layout(size fyne.Size) {
    topLeft := fyne.NewPos(0, 0)
    fcr.label.Move(topLeft)
    fcr.label.Resize(fyne.NewSize(fcr.label.MinSize().Width, size.Height))
    topLeft = topLeft.Add(fyne.NewPos(fcr.label.MinSize().Width, 0))
    fcr.entry.Move(topLeft)
    fcr.entry.Resize(fyne.NewSize(size.Width-fcr.label.MinSize().Width-fcr.button.MinSize().Width, size.Height))
    topLeft = topLeft.Add(fyne.NewPos(size.Width-fcr.label.MinSize().Width-fcr.button.MinSize().Width, 0))
    fcr.button.Move(topLeft)
    fcr.button.Resize(fyne.NewSize(fcr.button.MinSize().Width, size.Height))
}

func (fcr fileChoiceRender) Destroy() {}

func (fcr fileChoiceRender) Refresh() {}

func (fc FileChoice) CreateRenderer() fyne.WidgetRenderer {
    label := widget.NewLabel(fc.labelText)
    entry := widget.NewEntryWithData(fc.path)
    entry.PlaceHolder = fc.placeHolder
    button := widget.NewButton(fc.buttonText, func() {
        cwd, _ := os.Getwd()
        defer os.Chdir(cwd)
        file, err := dialog.File().Load()
        if err != nil {
            file = ""
        }
        if file != "" {
            entry.SetText(file)
        }
    })
    return &fileChoiceRender{label: label, entry: entry, button: button}
}

于 2021-11-14T20:38:07.543 回答