From bb78c11b07254699386f08e49fc969030a0d507f Mon Sep 17 00:00:00 2001 From: coder-in-go Date: Fri, 13 Dec 2024 16:27:58 +0530 Subject: [PATCH 1/5] Fixes issue 4770: Clicking on empty part of widget.List does not unfocus other widgets --- internal/driver/glfw/window.go | 16 ++++++++++++---- widget/list.go | 2 ++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/internal/driver/glfw/window.go b/internal/driver/glfw/window.go index 81b50a7b07..e6431300b2 100644 --- a/internal/driver/glfw/window.go +++ b/internal/driver/glfw/window.go @@ -511,7 +511,7 @@ func (w *window) processMouseClicked(button desktop.MouseButton, action action, co, pos, _ := w.findObjectAtPositionMatching(w.canvas, mousePos, func(object fyne.CanvasObject) bool { switch object.(type) { - case fyne.Tappable, fyne.SecondaryTappable, fyne.DoubleTappable, fyne.Focusable, desktop.Mouseable: + case fyne.Tappable, fyne.SecondaryTappable, fyne.DoubleTappable, fyne.Focusable, desktop.Mouseable, desktop.Hoverable: return true case fyne.Draggable: if mouseDragStarted { @@ -540,9 +540,11 @@ func (w *window) processMouseClicked(button desktop.MouseButton, action action, if wid, ok := co.(fyne.Focusable); !ok || wid != w.canvas.Focused() { ignore := false _, _, _ = w.findObjectAtPositionMatching(w.canvas, mousePos, func(object fyne.CanvasObject) bool { - switch object.(type) { - case fyne.Focusable: + if focusable, isFocusable := object.(fyne.Focusable); isFocusable { ignore = true + if focusable != w.canvas.Focused() { + w.canvas.Focus(focusable) + } return true } @@ -568,6 +570,10 @@ func (w *window) processMouseClicked(button desktop.MouseButton, action action, mousePressed := w.mousePressed w.mouseLock.Unlock() + if mousePressed == nil { + w.canvas.Unfocus() + } + if action == release && mouseDragged != nil { if mouseDragStarted { w.QueueEvent(mouseDragged.DragEnd) @@ -593,7 +599,9 @@ func (w *window) processMouseClicked(button desktop.MouseButton, action action, } else if action == release { if co == mousePressed { if button == desktop.MouseButtonSecondary && altTap { - w.QueueEvent(func() { secondary.TappedSecondary(ev) }) + w.QueueEvent(func() { + secondary.TappedSecondary(ev) + }) } } } diff --git a/widget/list.go b/widget/list.go index 90b7fa090f..5660957caa 100644 --- a/widget/list.go +++ b/widget/list.go @@ -130,6 +130,8 @@ func (l *List) RefreshItem(id ListItemID) { return } l.BaseWidget.Refresh() + + l.Unselect(id) lo := l.scroller.Content.(*fyne.Container).Layout.(*listLayout) lo.renderLock.RLock() // ensures we are not changing visible info in render code during the search item, ok := lo.searchVisible(lo.visible, id) From 9a34d2f4733d144bac6bc3de1f791cd396f09da4 Mon Sep 17 00:00:00 2001 From: coder-in-go Date: Fri, 13 Dec 2024 19:31:01 +0530 Subject: [PATCH 2/5] Added test case for the fix: 4770 --- internal/driver/glfw/window_test.go | 42 +++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/internal/driver/glfw/window_test.go b/internal/driver/glfw/window_test.go index 00c5e13222..37e6eea687 100644 --- a/internal/driver/glfw/window_test.go +++ b/internal/driver/glfw/window_test.go @@ -2061,3 +2061,45 @@ func (t *tabbable) AcceptsTab() bool { t.acceptTabCallCount++ return true } + +func TestProcessMouseClicked(t *testing.T) { + testApp := test.NewApp() + defer testApp.Quit() + + testWin := testApp.NewWindow("Test Window") + defer testWin.Close() + + tappable := &mockTappable{} + tappable.Resize(fyne.NewSize(100, 100)) + testWin.SetContent(tappable) + + test.Tap(tappable) + + if !tappable.tapped { + t.Errorf("Expected tappable object to be tapped") + } + + focusable := widget.NewEntry() + testWin.SetContent(container.NewVBox(tappable, focusable)) + + test.Tap(focusable) + + testWin.Canvas().Focus(focusable) + + if testWin.Canvas().Focused() != focusable { + t.Errorf("Expected focusable object to be focused") + } +} + +type mockTappable struct { + widget.BaseWidget + tapped bool +} + +func (m *mockTappable) Tapped(_ *fyne.PointEvent) { + m.tapped = true +} + +func (m *mockTappable) CreateRenderer() fyne.WidgetRenderer { + return widget.NewSimpleRenderer(widget.NewLabel("Tappable")) +} From 1e33f9b9fa1f4c48ddfc565695ca98577670ab86 Mon Sep 17 00:00:00 2001 From: coder-in-go Date: Wed, 18 Dec 2024 12:25:27 +0530 Subject: [PATCH 3/5] Reverted Added test case for the fix: 4770 --- internal/driver/glfw/window_test.go | 42 ----------------------------- 1 file changed, 42 deletions(-) diff --git a/internal/driver/glfw/window_test.go b/internal/driver/glfw/window_test.go index 37e6eea687..00c5e13222 100644 --- a/internal/driver/glfw/window_test.go +++ b/internal/driver/glfw/window_test.go @@ -2061,45 +2061,3 @@ func (t *tabbable) AcceptsTab() bool { t.acceptTabCallCount++ return true } - -func TestProcessMouseClicked(t *testing.T) { - testApp := test.NewApp() - defer testApp.Quit() - - testWin := testApp.NewWindow("Test Window") - defer testWin.Close() - - tappable := &mockTappable{} - tappable.Resize(fyne.NewSize(100, 100)) - testWin.SetContent(tappable) - - test.Tap(tappable) - - if !tappable.tapped { - t.Errorf("Expected tappable object to be tapped") - } - - focusable := widget.NewEntry() - testWin.SetContent(container.NewVBox(tappable, focusable)) - - test.Tap(focusable) - - testWin.Canvas().Focus(focusable) - - if testWin.Canvas().Focused() != focusable { - t.Errorf("Expected focusable object to be focused") - } -} - -type mockTappable struct { - widget.BaseWidget - tapped bool -} - -func (m *mockTappable) Tapped(_ *fyne.PointEvent) { - m.tapped = true -} - -func (m *mockTappable) CreateRenderer() fyne.WidgetRenderer { - return widget.NewSimpleRenderer(widget.NewLabel("Tappable")) -} From 8f277627bf67a5c90853376c4abfd2d6fe26d315 Mon Sep 17 00:00:00 2001 From: coder-in-go Date: Wed, 18 Dec 2024 12:26:26 +0530 Subject: [PATCH 4/5] Revert "Clicking on empty part of widget.List does not unfocus other widgets" --- internal/driver/glfw/window.go | 16 ++++------------ widget/list.go | 2 -- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/internal/driver/glfw/window.go b/internal/driver/glfw/window.go index e6431300b2..81b50a7b07 100644 --- a/internal/driver/glfw/window.go +++ b/internal/driver/glfw/window.go @@ -511,7 +511,7 @@ func (w *window) processMouseClicked(button desktop.MouseButton, action action, co, pos, _ := w.findObjectAtPositionMatching(w.canvas, mousePos, func(object fyne.CanvasObject) bool { switch object.(type) { - case fyne.Tappable, fyne.SecondaryTappable, fyne.DoubleTappable, fyne.Focusable, desktop.Mouseable, desktop.Hoverable: + case fyne.Tappable, fyne.SecondaryTappable, fyne.DoubleTappable, fyne.Focusable, desktop.Mouseable: return true case fyne.Draggable: if mouseDragStarted { @@ -540,11 +540,9 @@ func (w *window) processMouseClicked(button desktop.MouseButton, action action, if wid, ok := co.(fyne.Focusable); !ok || wid != w.canvas.Focused() { ignore := false _, _, _ = w.findObjectAtPositionMatching(w.canvas, mousePos, func(object fyne.CanvasObject) bool { - if focusable, isFocusable := object.(fyne.Focusable); isFocusable { + switch object.(type) { + case fyne.Focusable: ignore = true - if focusable != w.canvas.Focused() { - w.canvas.Focus(focusable) - } return true } @@ -570,10 +568,6 @@ func (w *window) processMouseClicked(button desktop.MouseButton, action action, mousePressed := w.mousePressed w.mouseLock.Unlock() - if mousePressed == nil { - w.canvas.Unfocus() - } - if action == release && mouseDragged != nil { if mouseDragStarted { w.QueueEvent(mouseDragged.DragEnd) @@ -599,9 +593,7 @@ func (w *window) processMouseClicked(button desktop.MouseButton, action action, } else if action == release { if co == mousePressed { if button == desktop.MouseButtonSecondary && altTap { - w.QueueEvent(func() { - secondary.TappedSecondary(ev) - }) + w.QueueEvent(func() { secondary.TappedSecondary(ev) }) } } } diff --git a/widget/list.go b/widget/list.go index 5660957caa..90b7fa090f 100644 --- a/widget/list.go +++ b/widget/list.go @@ -130,8 +130,6 @@ func (l *List) RefreshItem(id ListItemID) { return } l.BaseWidget.Refresh() - - l.Unselect(id) lo := l.scroller.Content.(*fyne.Container).Layout.(*listLayout) lo.renderLock.RLock() // ensures we are not changing visible info in render code during the search item, ok := lo.searchVisible(lo.visible, id) From 3b9c45158ebd9baa91649552b96594e3e790c098 Mon Sep 17 00:00:00 2001 From: coder-in-go Date: Wed, 18 Dec 2024 12:41:38 +0530 Subject: [PATCH 5/5] Fix for clicking on empty part of widget.List not unfocusing --- widget/list.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/widget/list.go b/widget/list.go index 90b7fa090f..1927fa854d 100644 --- a/widget/list.go +++ b/widget/list.go @@ -130,6 +130,7 @@ func (l *List) RefreshItem(id ListItemID) { return } l.BaseWidget.Refresh() + l.Unselect(id) lo := l.scroller.Content.(*fyne.Container).Layout.(*listLayout) lo.renderLock.RLock() // ensures we are not changing visible info in render code during the search item, ok := lo.searchVisible(lo.visible, id) @@ -386,6 +387,17 @@ func (l *List) contentMinSize() fyne.Size { return fyne.NewSize(l.itemMin.Width, height+separatorThickness*float32(items-1)) } +func (l *List) Tapped(event *fyne.PointEvent) { + canvas := fyne.CurrentApp().Driver().CanvasForObject(l) + if canvas != nil { + // First, unfocus the currently focused widget if any + if l.focused { + l.FocusLost() + } + canvas.Focus(l) + } +} + // fills l.visibleRowHeights and also returns offY and minRow func (l *listLayout) calculateVisibleRowHeights(itemHeight float32, length int, th fyne.Theme) (offY float32, minRow int) { rowOffset := float32(0)