Skip to content

Commit

Permalink
Merge pull request #750 from spatocode/selectentry/cursor
Browse files Browse the repository at this point in the history
Handle window cursor
  • Loading branch information
toaster committed Apr 4, 2020
2 parents 9c287e2 + 8565c1a commit ba019e9
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 14 deletions.
24 changes: 24 additions & 0 deletions driver/desktop/cursor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package desktop

// Cursor represents a standard fyne cursor
type Cursor int

const (
// DefaultCursor is the default cursor typically an arrow
DefaultCursor Cursor = iota
// TextCursor is the cursor often used to indicate text selection
TextCursor
// CrosshairCursor is the cursor often used to indicate bitmaps
CrosshairCursor
// PointerCursor is the cursor often used to indicate a link
PointerCursor
// HResizeCursor is the cursor often used to indicate horizontal resize
HResizeCursor
// VResizeCursor is the cursor often used to indicate vertical resize
VResizeCursor
)

// Cursorable describes any CanvasObject that needs a cursor change
type Cursorable interface {
Cursor() Cursor
}
39 changes: 25 additions & 14 deletions internal/driver/glfw/window.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"fyne.io/fyne/internal/cache"
"fyne.io/fyne/internal/driver"
"fyne.io/fyne/internal/painter/gl"
"fyne.io/fyne/widget"

"github.com/go-gl/glfw/v3.3/glfw"
)
Expand All @@ -29,22 +28,28 @@ const (
)

var (
defaultCursor, entryCursor, hyperlinkCursor *glfw.Cursor
initOnce = &sync.Once{}
defaultTitle = "Fyne Application"
cursorMap map[desktop.Cursor]*glfw.Cursor
initOnce = &sync.Once{}
defaultTitle = "Fyne Application"
)

func initCursors() {
defaultCursor = glfw.CreateStandardCursor(glfw.ArrowCursor)
entryCursor = glfw.CreateStandardCursor(glfw.IBeamCursor)
hyperlinkCursor = glfw.CreateStandardCursor(glfw.HandCursor)
cursorMap = map[desktop.Cursor]*glfw.Cursor{
desktop.DefaultCursor: glfw.CreateStandardCursor(glfw.ArrowCursor),
desktop.TextCursor: glfw.CreateStandardCursor(glfw.IBeamCursor),
desktop.CrosshairCursor: glfw.CreateStandardCursor(glfw.CrosshairCursor),
desktop.PointerCursor: glfw.CreateStandardCursor(glfw.HandCursor),
desktop.HResizeCursor: glfw.CreateStandardCursor(glfw.HResizeCursor),
desktop.VResizeCursor: glfw.CreateStandardCursor(glfw.VResizeCursor),
}
}

// Declare conformity to Window interface
var _ fyne.Window = (*window)(nil)

type window struct {
viewport *glfw.Window
cursor *glfw.Cursor
painted int // part of the macOS GL fix, updated GLFW should fix this
canvas *glCanvas
title string
Expand Down Expand Up @@ -509,23 +514,29 @@ func (w *window) findObjectAtPositionMatching(canvas *glCanvas, mouse fyne.Posit
return driver.FindObjectAtPositionMatching(mouse, matches, canvas.Overlays().Top(), roots...)
}

func fyneToNativeCursor(cursor desktop.Cursor) *glfw.Cursor {
ret, ok := cursorMap[cursor]
if !ok {
return cursorMap[desktop.DefaultCursor]
}
return ret
}

func (w *window) mouseMoved(viewport *glfw.Window, xpos float64, ypos float64) {
w.mousePos = fyne.NewPos(internal.UnscaleInt(w.canvas, int(xpos)), internal.UnscaleInt(w.canvas, int(ypos)))

cursor := defaultCursor
cursor := cursorMap[desktop.DefaultCursor]
obj, pos := w.findObjectAtPositionMatching(w.canvas, w.mousePos, func(object fyne.CanvasObject) bool {
if wid, ok := object.(*widget.Entry); ok {
if !wid.Disabled() {
cursor = entryCursor
}
} else if _, ok := object.(*widget.Hyperlink); ok {
cursor = hyperlinkCursor
if cursorable, ok := object.(desktop.Cursorable); ok {
fyneCursor := cursorable.Cursor()
cursor = fyneToNativeCursor(fyneCursor)
}

_, hover := object.(desktop.Hoverable)
return hover
})

w.cursor = cursor
viewport.SetCursor(cursor)
if obj != nil && !w.objIsDragged(obj) {
ev := new(desktop.MouseEvent)
Expand Down
23 changes: 23 additions & 0 deletions internal/driver/glfw/window_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package glfw

import (
"image/color"
"net/url"
"os"
"runtime"
"testing"
Expand Down Expand Up @@ -59,6 +60,28 @@ func TestGLDriver_CreateSplashWindow(t *testing.T) {
assert.True(t, w.centered)
}

func TestWindow_Cursor(t *testing.T) {
w := d.CreateWindow("Test").(*window)
e := widget.NewEntry()
u, _ := url.Parse("https://testing.fyne")
h := widget.NewHyperlink("Testing", u)
b := widget.NewButton("Test", nil)

w.SetContent(widget.NewVBox(e, h, b))

w.mouseMoved(w.viewport, 10, float64(e.Position().Y+10))
textCursor := cursorMap[desktop.TextCursor]
assert.Same(t, textCursor, w.cursor)

w.mouseMoved(w.viewport, 10, float64(h.Position().Y+10))
pointerCursor := cursorMap[desktop.PointerCursor]
assert.Same(t, pointerCursor, w.cursor)

w.mouseMoved(w.viewport, 10, float64(b.Position().Y+10))
defaultCursor := cursorMap[desktop.DefaultCursor]
assert.Same(t, defaultCursor, w.cursor)
}

func TestWindow_HandleHoverable(t *testing.T) {
w := d.CreateWindow("Test").(*window)
h1 := &hoverableObject{Rectangle: canvas.NewRectangle(color.White)}
Expand Down
5 changes: 5 additions & 0 deletions widget/button.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,11 @@ func (b *Button) SetText(text string) {
b.Refresh()
}

// Cursor returns the cursor type of this widget
func (b *Button) Cursor() desktop.Cursor {
return desktop.DefaultCursor
}

// SetIcon updates the icon on a label - pass nil to hide an icon
func (b *Button) SetIcon(icon fyne.Resource) {
b.Icon = icon
Expand Down
6 changes: 6 additions & 0 deletions widget/button_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"time"

"fyne.io/fyne"
"fyne.io/fyne/driver/desktop"
"fyne.io/fyne/test"
"fyne.io/fyne/theme"

Expand Down Expand Up @@ -42,6 +43,11 @@ func TestButton_MinSize_Icon(t *testing.T) {
assert.Equal(t, min2.Height, min1.Height)
}

func TestButton_Cursor(t *testing.T) {
button := NewButton("Test", nil)
assert.Equal(t, desktop.DefaultCursor, button.Cursor())
}

func TestButton_Style(t *testing.T) {
button := NewButton("Test", nil)
bg := test.WidgetRenderer(button).BackgroundColor()
Expand Down
9 changes: 9 additions & 0 deletions widget/entry.go
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,11 @@ func (e *Entry) TappedSecondary(pe *fyne.PointEvent) {
}
}

// Cursor returns the cursor type of this widget
func (e *Entry) Cursor() desktop.Cursor {
return desktop.TextCursor
}

// MouseDown called on mouse click, this triggers a mouse click which can move the cursor,
// update the existing selection (if shift is held), or start a selection dragging operation.
func (e *Entry) MouseDown(m *desktop.MouseEvent) {
Expand Down Expand Up @@ -1198,6 +1203,10 @@ func (pr *passwordRevealer) Tapped(*fyne.PointEvent) {
fyne.CurrentApp().Driver().CanvasForObject(pr).Focus(pr.entry)
}

func (pr *passwordRevealer) Cursor() desktop.Cursor {
return desktop.DefaultCursor
}

func newPasswordRevealer(e *Entry) *passwordRevealer {
pr := &passwordRevealer{
icon: canvas.NewImageFromResource(theme.VisibilityOffIcon()),
Expand Down
11 changes: 11 additions & 0 deletions widget/entry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,17 @@ func TestEntry_MinSize(t *testing.T) {
assert.Equal(t, min.Add(fyne.NewSize(theme.IconInlineSize()+theme.Padding(), 0)), entry.MinSize())
}

func TestEntry_Cursor(t *testing.T) {
entry := NewEntry()
assert.Equal(t, desktop.TextCursor, entry.Cursor())
}

func TestEntry_passwordRevealerCursor(t *testing.T) {
entry := NewEntry()
pr := newPasswordRevealer(entry)
assert.Equal(t, desktop.DefaultCursor, pr.Cursor())
}

func TestMultiLineEntry_MinSize(t *testing.T) {
entry := NewEntry()
singleMin := entry.MinSize()
Expand Down
6 changes: 6 additions & 0 deletions widget/hyperlink.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"net/url"

"fyne.io/fyne"
"fyne.io/fyne/driver/desktop"
"fyne.io/fyne/theme"
)

Expand Down Expand Up @@ -36,6 +37,11 @@ func NewHyperlinkWithStyle(text string, url *url.URL, alignment fyne.TextAlign,
return hl
}

// Cursor returns the cursor type of this widget
func (hl *Hyperlink) Cursor() desktop.Cursor {
return desktop.PointerCursor
}

// SetText sets the text of the hyperlink
func (hl *Hyperlink) SetText(text string) {
hl.Text = text
Expand Down
9 changes: 9 additions & 0 deletions widget/hyperlink_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"testing"

"fyne.io/fyne"
"fyne.io/fyne/driver/desktop"
_ "fyne.io/fyne/test"
"fyne.io/fyne/theme"
"github.com/stretchr/testify/assert"
Expand All @@ -23,6 +24,14 @@ func TestHyperlink_MinSize(t *testing.T) {
assert.True(t, hyperlink.MinSize().Width > min.Width)
}

func TestHyperlink_Cursor(t *testing.T) {
u, err := url.Parse("https://fyne.io/")
hyperlink := NewHyperlink("Test", u)

assert.Nil(t, err)
assert.Equal(t, desktop.PointerCursor, hyperlink.Cursor())
}

func TestHyperlink_Alignment(t *testing.T) {
hyperlink := &Hyperlink{Text: "Test", Alignment: fyne.TextAlignTrailing}
assert.Equal(t, fyne.TextAlignTrailing, textRenderTexts(hyperlink)[0].Alignment)
Expand Down

0 comments on commit ba019e9

Please sign in to comment.