Skip to content

Commit

Permalink
Add helper PixelCoordinateForPosition function in Canvas
Browse files Browse the repository at this point in the history
This is required for certain use-cases to know where in a render output a position is.
Cannot be done with current API due to Gl texture scale in glPainter on macOS
  • Loading branch information
andydotxyz committed Feb 7, 2020
1 parent 567a8a0 commit 672adce
Show file tree
Hide file tree
Showing 8 changed files with 76 additions and 4 deletions.
4 changes: 4 additions & 0 deletions canvas.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,8 @@ type Canvas interface {
AddShortcut(shortcut Shortcut, handler func(shortcut Shortcut))

Capture() image.Image

// PixelCoordinateForPosition returns the x and y pixel coordinate for a given position on this canvas.
// This can be used to find absolute pixel positions or pixel offsets relative to an object top left.
PixelCoordinateForPosition(Position) (int, int)
}
22 changes: 19 additions & 3 deletions internal/driver/glfw/canvas.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package glfw

import (
"image"
"math"
"sync"

"fyne.io/fyne"
Expand Down Expand Up @@ -32,8 +33,8 @@ type glCanvas struct {
onKeyUp func(*fyne.KeyEvent)
shortcut fyne.ShortcutHandler

scale, detectedScale float32
painter gl.Painter
scale, detectedScale, texScale float32
painter gl.Painter

dirty bool
dirtyMutex *sync.Mutex
Expand Down Expand Up @@ -192,6 +193,21 @@ func (c *glCanvas) SetScale(_ float32) {
c.context.RescaleContext()
}

func (c *glCanvas) setTextureScale(scale float32) {
c.texScale = scale
c.painter.SetFrameBufferScale(scale)
}

func (c *glCanvas) PixelCoordinateForPosition(pos fyne.Position) (int, int) {
texScale := c.texScale
multiple := float64(c.Scale() * texScale)
scaleInt := func(x int) int {
return int(math.Round(float64(x) * multiple))
}

return scaleInt(pos.X), scaleInt(pos.Y)
}

func (c *glCanvas) OnTypedRune() func(rune) {
return c.onTypedRune
}
Expand Down Expand Up @@ -463,7 +479,7 @@ func (c *glCanvas) contentPos() fyne.Position {
}

func newCanvas() *glCanvas {
c := &glCanvas{scale: 1.0}
c := &glCanvas{scale: 1.0, texScale: 1.0}
c.content = &canvas.Rectangle{FillColor: theme.BackgroundColor()}
c.contentTree = &renderCacheTree{root: &renderCacheNode{obj: c.content}}
c.padded = true
Expand Down
24 changes: 24 additions & 0 deletions internal/driver/glfw/canvas_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,30 @@ func TestGlCanvas_Resize(t *testing.T) {
assert.Equal(t, size, over.Size())
}

func TestGlCanvas_Scale(t *testing.T) {
w := d.CreateWindow("Test").(*window)
c := w.Canvas().(*glCanvas)

c.scale = 2.5
assert.Equal(t, 5, int(2*c.Scale()))
}

func TestGlCanvas_PixelCoordinateAtPosition(t *testing.T) {
w := d.CreateWindow("Test").(*window)
c := w.Canvas().(*glCanvas)

pos := fyne.NewPos(4, 4)
c.scale = 2.5
x, y := c.PixelCoordinateForPosition(pos)
assert.Equal(t, 10, x)
assert.Equal(t, 10, y)

c.texScale = 2.0
x, y = c.PixelCoordinateForPosition(pos)
assert.Equal(t, 20, x)
assert.Equal(t, 20, y)
}

func Test_glCanvas_SetContent(t *testing.T) {
fyne.CurrentApp().Settings().SetTheme(theme.DarkTheme())
var menuHeight int
Expand Down
2 changes: 1 addition & 1 deletion internal/driver/glfw/window.go
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ func (w *window) frameSized(viewport *glfw.Window, width, height int) {

winWidth, _ := w.viewport.GetSize()
texScale := float32(width) / float32(winWidth) // This will be > 1.0 on a HiDPI screen
w.canvas.painter.SetFrameBufferScale(texScale)
w.canvas.setTextureScale(texScale)
w.canvas.painter.SetOutputSize(width, height)
}

Expand Down
4 changes: 4 additions & 0 deletions internal/driver/gomobile/canvas.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ func (c *mobileCanvas) SetScale(_ float32) {
c.scale = fyne.CurrentDevice().SystemScale()
}

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) Overlay() fyne.CanvasObject {
return c.overlay
}
Expand Down
10 changes: 10 additions & 0 deletions internal/driver/gomobile/canvas_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ import (
"github.com/stretchr/testify/assert"
)

func TestCanvas_PixelCoordinateAtPosition(t *testing.T) {
c := NewCanvas().(*mobileCanvas)

pos := fyne.NewPos(4, 4)
c.scale = 2.5
x, y := c.PixelCoordinateForPosition(pos)
assert.Equal(t, 10, x)
assert.Equal(t, 10, y)
}

func TestCanvas_Tapped(t *testing.T) {
tapped := false
altTapped := false
Expand Down
4 changes: 4 additions & 0 deletions test/testcanvas.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ func (c *testCanvas) SetScale(scale float32) {
c.scale = scale
}

func (c *testCanvas) PixelCoordinateForPosition(pos fyne.Position) (int, int) {
return int(float32(pos.X) * c.scale), int(float32(pos.Y) * c.scale)
}

func (c *testCanvas) OnTypedRune() func(rune) {
return c.onTypedRune
}
Expand Down
10 changes: 10 additions & 0 deletions test/testcanvas_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,13 @@ func TestTestCanvas_Capture(t *testing.T) {
assert.Equal(t, b1, b2)
assert.Equal(t, a1, a2)
}

func TestGlCanvas_PixelCoordinateAtPosition(t *testing.T) {
c := NewCanvas().(*testCanvas)

pos := fyne.NewPos(4, 4)
c.scale = 2.5
x, y := c.PixelCoordinateForPosition(pos)
assert.Equal(t, 10, x)
assert.Equal(t, 10, y)
}

0 comments on commit 672adce

Please sign in to comment.