Skip to content

Commit 16ad916

Browse files
authored
Merge pull request #120 from laszukdawid/feat/completionentry-onfocusgained
widget: make CompletionEntry.FocusGained overridable
2 parents cbbdf56 + 48bc285 commit 16ad916

2 files changed

Lines changed: 52 additions & 4 deletions

File tree

widget/completionentry.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ type CompletionEntry struct {
1717

1818
CustomCreate func() fyne.CanvasObject
1919
CustomUpdate func(id widget.ListItemID, object fyne.CanvasObject)
20+
21+
impl fyne.Widget
2022
}
2123

2224
// NewCompletionEntry creates a new CompletionEntry which creates a popup menu that responds to keystrokes to navigate through the items without losing the editing ability of the text input.
@@ -26,6 +28,20 @@ func NewCompletionEntry(options []string) *CompletionEntry {
2628
return c
2729
}
2830

31+
// ExtendBaseWidget is used by an extending widget to make use of CompletionEntry functionality.
32+
func (c *CompletionEntry) ExtendBaseWidget(wid fyne.Widget) {
33+
c.impl = wid
34+
c.Entry.ExtendBaseWidget(wid)
35+
}
36+
37+
// super returns the extending widget if set, otherwise the CompletionEntry itself.
38+
func (c *CompletionEntry) super() fyne.Widget {
39+
if c.impl != nil {
40+
return c.impl
41+
}
42+
return c
43+
}
44+
2945
// HideCompletion hides the completion menu.
3046
func (c *CompletionEntry) HideCompletion() {
3147
if c.popupMenu != nil {
@@ -84,7 +100,7 @@ func (c *CompletionEntry) ShowCompletion() {
84100
c.navigableList.UnselectAll()
85101
c.navigableList.selected = -1
86102
}
87-
holder := fyne.CurrentApp().Driver().CanvasForObject(c)
103+
holder := fyne.CurrentApp().Driver().CanvasForObject(c.super())
88104

89105
if c.popupMenu == nil {
90106
c.popupMenu = widget.NewPopUp(c.navigableList, holder)
@@ -96,7 +112,7 @@ func (c *CompletionEntry) ShowCompletion() {
96112

97113
// calculate the max size to make the popup to cover everything below the entry
98114
func (c *CompletionEntry) maxSize() fyne.Size {
99-
cnv := fyne.CurrentApp().Driver().CanvasForObject(c)
115+
cnv := fyne.CurrentApp().Driver().CanvasForObject(c.super())
100116

101117
if c.itemHeight == 0 {
102118
// set item height to cache
@@ -105,7 +121,7 @@ func (c *CompletionEntry) maxSize() fyne.Size {
105121

106122
canvasSize := cnv.Size()
107123
entrySize := c.Size()
108-
entryPos := fyne.CurrentApp().Driver().AbsolutePositionForObject(c)
124+
entryPos := fyne.CurrentApp().Driver().AbsolutePositionForObject(c.super())
109125
listHeight := float32(len(c.Options))*(c.itemHeight+2*theme.Padding()+theme.SeparatorThicknessSize()) + 2*theme.Padding()
110126
maxHeight := canvasSize.Height - entryPos.Y - entrySize.Height - 2*theme.Padding()
111127

@@ -118,7 +134,7 @@ func (c *CompletionEntry) maxSize() fyne.Size {
118134

119135
// calculate where the popup should appear
120136
func (c *CompletionEntry) popUpPos() fyne.Position {
121-
entryPos := fyne.CurrentApp().Driver().AbsolutePositionForObject(c)
137+
entryPos := fyne.CurrentApp().Driver().AbsolutePositionForObject(c.super())
122138
return entryPos.Add(fyne.NewPos(0, c.Size().Height))
123139
}
124140

widget/completionentry_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"testing"
55

66
"fyne.io/fyne/v2"
7+
"fyne.io/fyne/v2/container"
78
"fyne.io/fyne/v2/test"
89
"fyne.io/fyne/v2/widget"
910
"github.com/stretchr/testify/assert"
@@ -228,3 +229,34 @@ func TestCompletionEntry_DoubleSubmissionIssue(t *testing.T) {
228229
win.Canvas().Focused().TypedKey(&fyne.KeyEvent{Name: fyne.KeyReturn}) // OnSubmitted should be called
229230
assert.True(t, submitted)
230231
}
232+
233+
// focusCompletionEntry overrides FocusGained to show the completion menu.
234+
type focusCompletionEntry struct {
235+
CompletionEntry
236+
}
237+
238+
func (f *focusCompletionEntry) FocusGained() {
239+
f.CompletionEntry.FocusGained()
240+
f.ShowCompletion()
241+
}
242+
243+
// When CompletionEntry is embedded, the popup must resolve its position against
244+
// the extending widget so it appears below the entry rather than at the origin.
245+
func TestCompletionEntry_SubclassFocusGained(t *testing.T) {
246+
entry := &focusCompletionEntry{}
247+
entry.Options = entryData
248+
entry.ExtendBaseWidget(entry)
249+
250+
// Nest below a label so the entry's absolute position is non-zero.
251+
win := test.NewWindow(container.NewVBox(widget.NewLabel("top"), entry))
252+
win.Resize(fyne.NewSize(500, 300))
253+
defer win.Close()
254+
255+
win.Canvas().Focus(entry)
256+
assert.NotNil(t, entry.popupMenu)
257+
assert.True(t, entry.popupMenu.Visible())
258+
259+
entryPos := fyne.CurrentApp().Driver().AbsolutePositionForObject(entry)
260+
assert.Greater(t, entryPos.Y, float32(0))
261+
assert.Equal(t, entryPos.Add(fyne.NewPos(0, entry.Size().Height)), entry.popUpPos())
262+
}

0 commit comments

Comments
 (0)