diff --git a/internal/driver/mobile/canvas.go b/internal/driver/mobile/canvas.go index 65d3dc7bbb..a0b1212c57 100644 --- a/internal/driver/mobile/canvas.go +++ b/internal/driver/mobile/canvas.go @@ -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 } @@ -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 } @@ -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) { @@ -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) @@ -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) } } diff --git a/internal/driver/mobile/driver.go b/internal/driver/mobile/driver.go index 565aa9fdeb..941ad4eebc 100644 --- a/internal/driver/mobile/driver.go +++ b/internal/driver/mobile/driver.go @@ -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) } diff --git a/internal/driver/mobile/driver_test.go b/internal/driver/mobile/driver_test.go new file mode 100644 index 0000000000..d7ef23e627 --- /dev/null +++ b/internal/driver/mobile/driver_test.go @@ -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) + }) + } +}