Skip to content

Commit

Permalink
Merge pull request #4857 from toaster/bugfix/mobile_absolute_position
Browse files Browse the repository at this point in the history
fix mobile mobile driver absolute position computation for padded child windows
  • Loading branch information
toaster committed May 23, 2024
2 parents 32e868e + eea39e2 commit 09c078f
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 43 deletions.
87 changes: 49 additions & 38 deletions internal/driver/mobile/canvas.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ func (c *mobileCanvas) InteractiveArea() (fyne.Position, fyne.Size) {
c.size.SubtractWidthHeight(safeLeft+safeRight, safeTop+safeBottom)
}

func (c *mobileCanvas) MinSize() fyne.Size {
return c.size // TODO check
}

func (c *mobileCanvas) OnTypedKey() func(*fyne.KeyEvent) {
return c.onTypedKey
}
Expand All @@ -93,6 +97,14 @@ func (c *mobileCanvas) PixelCoordinateForPosition(pos fyne.Position) (int, int)
return int(float32(pos.X) * c.scale), int(float32(pos.Y) * c.scale)
}

func (c *mobileCanvas) Resize(size fyne.Size) {
if size == c.size {
return
}

c.sizeContent(size)
}

func (c *mobileCanvas) Scale() float32 {
return c.scale
}
Expand All @@ -115,8 +127,29 @@ func (c *mobileCanvas) Size() fyne.Size {
return c.size
}

func (c *mobileCanvas) MinSize() fyne.Size {
return c.size // TODO check
func (c *mobileCanvas) applyThemeOutOfTreeObjects() {
if c.menu != nil {
app.ApplyThemeTo(c.menu, c) // Ensure our menu gets the theme change message as it's out-of-tree
}
if c.windowHead != nil {
app.ApplyThemeTo(c.windowHead, c) // Ensure our child windows get the theme change message as it's out-of-tree
}
}

func (c *mobileCanvas) chromeBoxVerticalOffset() float32 {
if c.windowHead == nil {
return 0
}

chromeBox := c.windowHead.(*fyne.Container)
if c.padded {
chromeBox = chromeBox.Objects[0].(*fyne.Container) // the padded container
}
if len(chromeBox.Objects) > 1 {
return c.windowHead.MinSize().Height
}

return 0
}

func (c *mobileCanvas) findObjectAtPositionMatching(pos fyne.Position, test func(object fyne.CanvasObject) bool) (fyne.CanvasObject, fyne.Position, int) {
Expand Down Expand Up @@ -148,14 +181,6 @@ func (c *mobileCanvas) overlayChanged() {
c.SetDirty()
}

func (c *mobileCanvas) Resize(size fyne.Size) {
if size == c.size {
return
}

c.sizeContent(size)
}

func (c *mobileCanvas) setContent(content fyne.CanvasObject) {
c.content = content
c.SetContentTreeAndFocusMgr(content)
Expand All @@ -174,59 +199,45 @@ func (c *mobileCanvas) setWindowHead(head fyne.CanvasObject) {
c.SetMobileWindowHeadTree(head)
}

func (c *mobileCanvas) applyThemeOutOfTreeObjects() {
if c.menu != nil {
app.ApplyThemeTo(c.menu, c) // Ensure our menu gets the theme change message as it's out-of-tree
}
if c.windowHead != nil {
app.ApplyThemeTo(c.windowHead, c) // Ensure our child windows get the theme change message as it's out-of-tree
}
}

func (c *mobileCanvas) sizeContent(size fyne.Size) {
if c.content == nil { // window may not be configured yet
return
}
c.size = size

offset := fyne.NewPos(0, 0)
chromeBoxOffset := c.chromeBoxVerticalOffset()
areaPos, areaSize := c.InteractiveArea()

if c.windowHead != nil {
topHeight := c.windowHead.MinSize().Height

chromeBox := c.windowHead.(*fyne.Container)
if c.padded {
chromeBox = chromeBox.Objects[0].(*fyne.Container) // the padded container
}
if len(chromeBox.Objects) > 1 {
c.windowHead.Resize(fyne.NewSize(areaSize.Width, topHeight))
offset = fyne.NewPos(0, topHeight)
areaSize = areaSize.Subtract(offset)
var headSize fyne.Size
if chromeBoxOffset > 0 {
headSize = fyne.NewSize(areaSize.Width, chromeBoxOffset)
} else {
c.windowHead.Resize(c.windowHead.MinSize())
headSize = c.windowHead.MinSize()
}
c.windowHead.Resize(headSize)
c.windowHead.Move(areaPos)
}

topLeft := areaPos.Add(offset)
contentPos := areaPos.AddXY(0, chromeBoxOffset)
contentSize := areaSize.SubtractWidthHeight(0, chromeBoxOffset)
for _, overlay := range c.Overlays().List() {
if p, ok := overlay.(*widget.PopUp); ok {
// TODO: remove this when #707 is being addressed.
// “Notifies” the PopUp of the canvas size change.
p.Refresh()
} else {
overlay.Resize(areaSize)
overlay.Move(topLeft)
overlay.Resize(contentSize)
overlay.Move(contentPos)
}
}

if c.padded {
c.content.Resize(areaSize.Subtract(fyne.NewSize(theme.Padding()*2, theme.Padding()*2)))
c.content.Move(topLeft.Add(fyne.NewPos(theme.Padding(), theme.Padding())))
c.content.Resize(contentSize.Subtract(fyne.NewSize(theme.Padding()*2, theme.Padding()*2)))
c.content.Move(contentPos.Add(fyne.NewPos(theme.Padding(), theme.Padding())))
} else {
c.content.Resize(areaSize)
c.content.Move(topLeft)
c.content.Resize(contentSize)
c.content.Move(contentPos)
}
}

Expand Down
7 changes: 2 additions & 5 deletions internal/driver/mobile/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,8 @@ func (d *mobileDriver) AbsolutePositionForObject(co fyne.CanvasObject) fyne.Posi
pos := driver.AbsolutePositionForObject(co, mc.ObjectTrees())
inset, _ := c.InteractiveArea()

if mc.windowHead != nil {
if len(mc.windowHead.(*fyne.Container).Objects) > 1 {
topHeight := mc.windowHead.MinSize().Height
pos = pos.Subtract(fyne.NewSize(0, topHeight))
}
if chromeBoxOffset := mc.chromeBoxVerticalOffset(); chromeBoxOffset > 0 {
pos = pos.SubtractXY(0, chromeBoxOffset)
}
return pos.Subtract(inset)
}
Expand Down
82 changes: 82 additions & 0 deletions internal/driver/mobile/driver_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package mobile

import (
"image/color"
"testing"

"github.com/stretchr/testify/assert"

"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
)

func Test_mobileDriver_AbsolutePositionForObject(t *testing.T) {
for name, tt := range map[string]struct {
want fyne.Position
windowIsChild bool
windowPadded bool
}{
"for an unpadded primary (non-child) window it is (0,0)": {
want: fyne.NewPos(0, 0),
windowIsChild: false,
windowPadded: false,
},
"for a padded primary (non-child) window it is (padding,padding)": {
want: fyne.NewPos(4, 4),
windowIsChild: false,
windowPadded: true,
},
"for an unpadded child window it is (0,0)": {
want: fyne.NewPos(0, 0),
windowIsChild: true,
windowPadded: false,
},
"for a padded child window it is (padding,padding)": {
want: fyne.NewPos(4, 4),
windowIsChild: true,
windowPadded: true,
},
} {
t.Run(name, func(t *testing.T) {
var o fyne.CanvasObject
size := fyne.NewSize(100, 100)
d := &mobileDriver{}
w := d.CreateWindow("main")
w.SetPadded(tt.windowPadded)
l := widget.NewLabel("main window")
if !tt.windowIsChild {
o = l
}
w.SetContent(l)
w.Show()
w.Resize(size)
w = d.CreateWindow("child1")
w.SetContent(widget.NewLabel("first child"))
if tt.windowIsChild {
w.Show()
}
w.Resize(size)
w = d.CreateWindow("child2 - hidden")
w.SetContent(widget.NewLabel("second child"))
w.Resize(size)
w = d.CreateWindow("child3")
r := canvas.NewRectangle(color.White)
r.SetMinSize(fyne.NewSize(42, 17))
w.SetPadded(tt.windowPadded)
w.SetContent(container.NewVBox(r))
if tt.windowIsChild {
w.Show()
o = r
}
w.Resize(size)
w = d.CreateWindow("child4 - hidden")
w.SetContent(widget.NewLabel("fourth child"))
w.Resize(size)

got := d.AbsolutePositionForObject(o)
assert.Equal(t, tt.want, got)
})
}
}

0 comments on commit 09c078f

Please sign in to comment.