From 3b7e52a4796bb45fa301291450951483a7bb59ed Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Thu, 4 Feb 2021 19:59:24 +0000 Subject: [PATCH] Fix issue with Focus call crashing Fixes #1893 --- internal/app/focus_manager.go | 12 ++++++++++++ internal/driver/glfw/canvas.go | 13 ++++++++++++- internal/driver/glfw/canvas_test.go | 11 +++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/internal/app/focus_manager.go b/internal/app/focus_manager.go index 1eff4bb605..668aa12520 100644 --- a/internal/app/focus_manager.go +++ b/internal/app/focus_manager.go @@ -59,6 +59,18 @@ func (f *FocusManager) Focus(obj fyne.Focusable) bool { return true } +// FocusBeforeAdded allows an object to be focused before it is added to the object tree. +// This is typically used before a canvas is visible and should be used with care. +func (f *FocusManager) FocusBeforeAdded(obj fyne.Focusable) { + f.RLock() + defer f.RUnlock() + if dis, ok := obj.(fyne.Disableable); ok && dis.Disabled() { + return + } + + f.focus(obj) +} + // Focused returns the currently focused object or nil if none. func (f *FocusManager) Focused() fyne.Focusable { f.RLock() diff --git a/internal/driver/glfw/canvas.go b/internal/driver/glfw/canvas.go index 265763cc3f..8f46a65e49 100644 --- a/internal/driver/glfw/canvas.go +++ b/internal/driver/glfw/canvas.go @@ -84,13 +84,17 @@ func (c *glCanvas) Focus(obj fyne.Focusable) { c.RUnlock() for _, mgr := range focusMgrs { + if mgr == nil { + continue + } if focusMgr != mgr { if mgr.Focus(obj) { return } } } - fyne.LogError("Failed to focus object which is not part of the canvas’ content, menu or overlays.", nil) + + c.contentFocusMgr.FocusBeforeAdded(obj) // not found yet assume we are preparing the UI } func (c *glCanvas) Focused() fyne.Focusable { @@ -452,7 +456,14 @@ func (c *glCanvas) paint(size fyne.Size) { func (c *glCanvas) setContent(content fyne.CanvasObject) { c.content = content c.contentTree = &renderCacheTree{root: &renderCacheNode{obj: c.content}} + var focused fyne.Focusable + if c.contentFocusMgr != nil { + focused = c.contentFocusMgr.Focused() // keep old focus if possible + } c.contentFocusMgr = app.NewFocusManager(c.content) + if focused != nil { + c.contentFocusMgr.Focus(focused) + } } func (c *glCanvas) setDirty(dirty bool) { diff --git a/internal/driver/glfw/canvas_test.go b/internal/driver/glfw/canvas_test.go index f5f6ccfe0e..8189b73f38 100644 --- a/internal/driver/glfw/canvas_test.go +++ b/internal/driver/glfw/canvas_test.go @@ -204,6 +204,17 @@ func TestGlCanvas_Focus(t *testing.T) { assert.True(t, o2e.focused) } +func TestGlCanvas_Focus_BeforeVisible(t *testing.T) { + w := createWindow("Test") + w.SetPadded(false) + e := widget.NewEntry() + c := w.Canvas().(*glCanvas) + c.Focus(e) // this crashed in the past + + w.SetContent(e) + assert.Equal(t, e, c.Focused(), "Item set to focus before SetContent was not focused after") +} + func TestGlCanvas_FocusHandlingWhenAddingAndRemovingOverlays(t *testing.T) { w := createWindow("Test") w.SetPadded(false)