问题描述
(这里有许多新的细节.)
我正在尝试(非常努力)扩展少数 Fyne 小部件的功能.基本上我需要实现或覆盖"右键单击选择、按钮、编辑等内容.我还需要对其他一些行为做一些细微的改变.
我尝试以各种方式扩展现有的小部件.我所做的一切都会导致编译失败、恐慌或奇怪和不正确的行为.下面的答案提供了 Button 解决方案的声明,但它不完整.当我将相同的技术应用于 Select 时,尝试左键单击时它会发生恐慌.
恐慌是
panic:接口转换:fyne.Canvas 为零,而不是 *glfw.glCanvas
回想起来,这就是我最初失败的地方,也是促使我尝试越来越复杂的方法的原因,但没有一个奏效.Button 和 Select 显然有不同的要求.
(很抱歉不接受",但另一种选择是尝试数小时并且对我尝试的东西没有信心 - 即使它避免了恐慌 - 是正确和长期的解决方案.声称 Fyne 小部件是可扩展的- 要求改进文档以说明如何改进似乎是合理的,因为幼稚的尝试并不总是有效.)
在这里,我尝试使用答案中的技术扩展我关心的小部件.我原以为所有这些小部件都会以相同的方式运行,以 TappedSecondary 的覆盖为模.他们一般不会.正确答案显然比下面的答案复杂得多.这是我根据下面给出的代码得出的结论:
扩展按钮(如答案所示):widget.Button 在鼠标进入该区域时会改变颜色.扩展版没有.两种形式都不会在单击时更改按钮的外观(我通常希望从任何 GUI 中都能看到,也许缺少其他东西?)扩展版本确实捕获了 TappedSecondary.
Extending Select(如上图所示):左键点击恐慌.右键单击被正确捕获.
扩展标签:你必须添加一个无所事事的 Tapped 函数来让它匹配所需的界面,但右键点击可以被捕获,一切似乎都很好.
扩展条目:它捕获 TappedSecondary,正如预期的那样,这意味着我没有得到通常的下拉菜单.左键单击(移动光标)不会在新位置重绘光标,但内部光标位置会发生变化(退格键会移除新位置的字符,然后正确重绘所有内容).
扩展检查:TappedSecondary 被捕获.左键单击导致 OnChanged 被调用(并且每次正确传递的布尔值都会交替)但从未绘制复选标记.
据我今天所知,这些是我需要扩展的唯一小部件,所以当它使列出的小部件正常工作时,我认为答案是完整的,除非我得到 TappedSecondary.理想的答案是指向在线某处的新文档,其中提供了所有小部件类型的完整示例,并以保留现有行为的方式(除非您故意覆盖该行为.)
总体印象:以最简单的方式扩展小部件可能会改变或破坏小部件在响应鼠标事件时绘制小部件的某些方面,包括来自 Select 的恐慌以及缺少 Entry、Button 和 Check 中的图形更改.我没有覆盖任何现有的 Tapped() 函数,所以我不知道为什么会这样.
//关于包含名称串、值串等我们不关心的字段输入 HasAbout 接口 {GetAbout() *关于}//失去对鼠标移动的反应输入 myButton 结构 {小部件.按钮关于 *关于}func (m *myButton) TappedSecondary(*fyne.PointEvent) {log.Println("右键单击")//有效}func (m *myButton) GetAbout() *About {返回 m.about}func newMyButton(label string, tapped func()) *myButton {ret := &myButton{}ret.ExtendBaseWidget(ret)ret.Text = 标签ret.OnTapped = 敲击返回 ret}//左键点击恐慌输入 mySelect 结构 {小部件.选择关于 *关于}func (m *mySelect) TappedSecondary(at *fyne.PointEvent) {log.Println("Right Click", m.about.name, *at)//有效}func (m *mySelect) GetAbout() *About {返回 m.about}func newMySelect(about *About) *mySelect {ret := &mySelect{}ret.ExtendBaseWidget(ret)ret.about = 关于//widget.Renderer(ret).Layout(ret.MinSize())//来自NewSelect,但没有修复panic返回 ret}//必须覆盖 Tapped,然后一切看起来都很好输入 myLabel 结构 {小部件.标签关于 *关于}func (m *myLabel) TappedSecondary(at *fyne.PointEvent) {log.Println("右键单击标签", m.GetAbout().name, *at)}//也必须实现这个,否则我们不会得到 TappedSecondaryfunc (m *myLabel) Tapped(at *fyne.PointEvent) {}func (m *myLabel) GetAbout() *About {返回 m.about}func newMyLabel(about *About) *myLabel {ret := &myLabel{}ret.ExtendBaseWidget(ret)ret.about = 关于ret.Text = about.value返回 ret}//无法在左键单击时查看光标变化.//正确地,在右键单击时丢失通常的下拉菜单输入 myEntry 结构 {小部件.Entry关于 *关于}func (m *myEntry) TappedSecondary(at *fyne.PointEvent) {log.Println("右键输入", m.Text, *at)}func (m *myEntry) GetAbout() *About {返回 m.about}func newMyEntry(about *About) *myEntry {ret := &myEntry{}ret.ExtendBaseWidget(ret)ret.about = 关于返回 ret}//无法查看您通过左键单击所做的检查更改.输入 myCheck 结构 {小部件.检查关于 *关于}func (m *myCheck) TappedSecondary(at *fyne.PointEvent) {log.Println("Right Click myCheck", m.Text, *at)//有效}func (m *myCheck) GetAbout() *About {返回 m.about}func newMyCheck(about *About) *myCheck {ret := &myCheck{}ret.ExtendBaseWidget(ret)ret.about = 关于ret.Text = about.valueret.OnChanged = func(v bool) {fmt.Println("Check is ", v)}//有效返回 ret}//驱动程序,来自原始答案,给出新的测试功能主(){关于 := 关于{}about.name = "你好"about.value = "呜呜"a := app.New()w := a.NewWindow("你好")w.SetContent(widget.NewVBox(newMyButton("右击我", func() {log.Println("正常回调")}),newMySelect(&about),newMyLabel(&about),newMyEntry(&about),newMyCheck(&about),widget.NewButton("X", func(){}) ,//比较),)w.ShowAndRun()}从库中复制代码在这里会让人分心 - 扩展小部件是完全可能的,所以我将回答最初的意图而不是修复错误...
您可以通过将核心小部件嵌入到结构中然后添加或覆盖功能来扩展它.关键是调用 ExtendBaseWidget
以便内部正确注册您的覆盖类型.下面的代码展示了如何创建一个响应右键单击的新按钮.
package main进口 (日志"fyne.io/fyne"fyne.io/fyne/app"fyne.io/fyne/widget")输入 myButton 结构 {小部件.按钮}func (m *myButton) TappedSecondary(*fyne.PointEvent) {log.Println("右键单击")}func newMyButton(label string, tapped func()) *myButton {ret := &myButton{}ret.ExtendBaseWidget(ret)ret.Text = 标签ret.OnTapped = 敲击返回 ret}功能主(){a := app.New()w := a.NewWindow("你好")w.SetContent(widget.NewVBox(newMyButton("右击我", func() {log.Println("正常回调")}),))w.ShowAndRun()}
(Many new details here.)
I am trying (very hard) to extend the functionality of a handful of Fyne's widgets. Basically I need to implement or "override" right click on things like Select, Button, Edit and so on. I need minor changes to some other behaviours as well.
I've tried extending existing widgets in various ways. Everything I do results in a compile failure, a panic, or bizarre and incorrect behaviour. The answer below provides a claim of a solution for Button, but it's incomplete. And when I apply the same technique to Select, it panics when left click is tried.
The panic is
panic: interface conversion: fyne.Canvas is nil, not *glfw.glCanvas
In retrospect, this is where I failed originally and is what prompted me to try increasingly complex approaches, none of which worked. Button and Select clearly have different requirements.
(Sorry to "unaccept", but the alternative is hours of trying things and having no confidence that what I tried - even if it avoids the panic - is the right and long term solution. The claim is that Fyne widgets are extensible - it seems reasonable to ask that the documentation be improved to show how, because naive attempts don't uniformly work.)
Here I try to extend the widgets I care about using the technique in the answer. I'd have thought that all these widgets would have acted identically, modulo the override of TappedSecondary. They generally don't. The right answer is obviously far more complex than presented in the answer below. Here's my findings based on the code given below:
Extending button (as shown in answer): widget.Button changes color when the mouse enters the area. The extended version does not. Neither form changes the appearance of the button when clicked (which I'd expect in general from any GUI, maybe something else is missing?) The extended version does catch TappedSecondary.
Extending Select (as shown above): left click panics. Right click is caught properly.
Extending Label: you have to add a do-nothing Tapped function to get it to match the interface needed, but right click can then be caught and all seems well.
Extending Entry: it catches TappedSecondary, which as expected means I don't get the usual dropdown menu. Left click (to move the cursor) doesn't redraw the cursor in the new location, but the internal cursor position does change (backspace will remove the character at the new position, and then redraw everything correctly).
Extending Check: TappedSecondary is caught. left-clicking causes OnChanged to be called (and the boolean passed properly alternates each time) but the check mark is never drawn.
As far as I know today these are the only widgets I need to extend, so I'd consider the answer complete when it makes the listed widgets work as they normally do except I get TappedSecondary. The ideal answer would point to new documentation online somewhere that gives complete examples, for all widget types, in a way that preserves the existing behaviour (unless you deliberately override that behaviour.)
Overall impression: extending a widget in the most trivial way possible can alter or break some aspects of drawing the widget when it responds to mouse events, including a panic from Select and missing graphical changes in Entry, Button and Check. I'm not overriding any existing Tapped() function so I don't know why this is the case.
//About contains name string, value string and other fields we don't care about
type HasAbout interface {
GetAbout() *About
}
//loses reaction to mouse movement
type myButton struct {
widget.Button
about *About
}
func (m *myButton) TappedSecondary(*fyne.PointEvent) {
log.Println("Right Click") //works
}
func (m *myButton) GetAbout() *About {
return m.about
}
func newMyButton(label string, tapped func()) *myButton {
ret := &myButton{}
ret.ExtendBaseWidget(ret)
ret.Text = label
ret.OnTapped = tapped
return ret
}
//panic on left click
type mySelect struct {
widget.Select
about *About
}
func (m *mySelect) TappedSecondary(at *fyne.PointEvent) {
log.Println("Right Click", m.about.name, *at) //works
}
func (m *mySelect) GetAbout() *About {
return m.about
}
func newMySelect(about *About) *mySelect {
ret := &mySelect{}
ret.ExtendBaseWidget(ret)
ret.about = about
//widget.Renderer(ret).Layout(ret.MinSize()) //from NewSelect, but did not fix panic
return ret
}
//must override Tapped, and then all seems well
type myLabel struct {
widget.Label
about *About
}
func (m *myLabel) TappedSecondary(at *fyne.PointEvent) {
log.Println("Right Click Label", m.GetAbout().name, *at)
}
//must also implement this or we don't get TappedSecondary
func (m *myLabel) Tapped(at *fyne.PointEvent) {
}
func (m *myLabel) GetAbout() *About {
return m.about
}
func newMyLabel(about *About) *myLabel {
ret := &myLabel{}
ret.ExtendBaseWidget(ret)
ret.about = about
ret.Text = about.value
return ret
}
//lose the ability to see cursor changes on left click.
//correctly, lose the usual dropdown menu on right click
type myEntry struct {
widget.Entry
about *About
}
func (m *myEntry) TappedSecondary(at *fyne.PointEvent) {
log.Println("Right Click Entry", m.Text, *at)
}
func (m *myEntry) GetAbout() *About {
return m.about
}
func newMyEntry(about *About) *myEntry {
ret := &myEntry{}
ret.ExtendBaseWidget(ret)
ret.about = about
return ret
}
//lose the ability to see check changes you made with left click.
type myCheck struct {
widget.Check
about *About
}
func (m *myCheck) TappedSecondary(at *fyne.PointEvent) {
log.Println("Right Click myCheck", m.Text, *at) //works
}
func (m *myCheck) GetAbout() *About {
return m.about
}
func newMyCheck(about *About) *myCheck {
ret := &myCheck{}
ret.ExtendBaseWidget(ret)
ret.about = about
ret.Text = about.value
ret.OnChanged = func(v bool) {fmt.Println("Check is ", v)} //works
return ret
}
//driver, from original answer, given new tests
func main() {
about := About{}
about.name = "hi"
about.value = "Whee"
a := app.New()
w := a.NewWindow("Hello")
w.SetContent(widget.NewVBox(
newMyButton("Right tap me", func() {
log.Println("Normal callback")
}),
newMySelect(&about),
newMyLabel(&about),
newMyEntry(&about),
newMyCheck(&about),
widget.NewButton("X", func(){}) , //to compare against
),
)
w.ShowAndRun()
}
Copying code out of the library is a distraction here - it is entirely possible to extend the widgets, so I will answer the original intent rather than fix the errors...
You can extend a core widget by embedding it in a struct then adding or overriding functionality. The key is to call ExtendBaseWidget
so that the internals register your overriding type correctly. The following code shows how to make a new button that responds to right click.
package main
import (
"log"
"fyne.io/fyne"
"fyne.io/fyne/app"
"fyne.io/fyne/widget"
)
type myButton struct {
widget.Button
}
func (m *myButton) TappedSecondary(*fyne.PointEvent) {
log.Println("Right Click")
}
func newMyButton(label string, tapped func()) *myButton {
ret := &myButton{}
ret.ExtendBaseWidget(ret)
ret.Text = label
ret.OnTapped = tapped
return ret
}
func main() {
a := app.New()
w := a.NewWindow("Hello")
w.SetContent(widget.NewVBox(
newMyButton("Right tap me", func() {
log.Println("Normal callback")
}),
))
w.ShowAndRun()
}
这篇关于未能扩展 fyne 的小部件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!