-
-
+
+
-
@@ -96,9 +95,9 @@ It should look like this:
# Documentation
-More documentation is available at the [Fyne developer website](https://fyne.io/develop/) or on [godoc.org](https://godoc.org/fyne.io/fyne).
+More documentation is available at the [Fyne developer website](https://fyne.io/develop/) or on [pkg.go.dev](https://pkg.go.dev/fyne.io/fyne?tab=doc).
# Examples
You can find many example applications in the [examples repository](https://github.com/fyne-io/examples/).
-Alternatively a list of applications using fyne can be found at [our website](https://apps.fyne.io/)
+Alternatively a list of applications using fyne can be found at [our website](https://apps.fyne.io/).
diff --git a/vendor/fyne.io/fyne/app.go b/vendor/fyne.io/fyne/app.go
index 407f5924..bc3208dd 100644
--- a/vendor/fyne.io/fyne/app.go
+++ b/vendor/fyne.io/fyne/app.go
@@ -31,8 +31,8 @@ type App interface {
// Calling Quit on the application will cause the application to exit
// cleanly, closing all open windows.
- // This should be used with caution, many platforms discourage exiting an application
- // from within the code and some mobile systems do not allow it.
+ // This function does no thing on a mobile device as the application lifecycle is
+ // managed by the operating system.
Quit()
// Driver returns the driver that is rendering this application.
diff --git a/vendor/fyne.io/fyne/canvasobject.go b/vendor/fyne.io/fyne/canvasobject.go
index 3bfc6399..f676c076 100644
--- a/vendor/fyne.io/fyne/canvasobject.go
+++ b/vendor/fyne.io/fyne/canvasobject.go
@@ -17,7 +17,9 @@ type CanvasObject interface {
// visibility
Visible() bool
+ // Show shows this object.
Show()
+ // Hide hides this object.
Hide()
Refresh()
diff --git a/vendor/fyne.io/fyne/go.mod b/vendor/fyne.io/fyne/go.mod
index 067a5b6a..56731ec0 100644
--- a/vendor/fyne.io/fyne/go.mod
+++ b/vendor/fyne.io/fyne/go.mod
@@ -21,4 +21,4 @@ require (
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb // indirect
)
-replace golang.org/x/mobile => github.com/fyne-io/mobile v0.0.0-20191204153723-f14fb9562406
+replace golang.org/x/mobile => github.com/fyne-io/mobile v0.0.0-20200218100723-2a24c20a57c6
diff --git a/vendor/fyne.io/fyne/go.sum b/vendor/fyne.io/fyne/go.sum
index f082144c..9428b6d1 100644
--- a/vendor/fyne.io/fyne/go.sum
+++ b/vendor/fyne.io/fyne/go.sum
@@ -6,8 +6,8 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/fyne-io/mobile v0.0.0-20191204153723-f14fb9562406 h1:hPlyLwTtVZdcq9+N3Sdv8d9DJVfieq8Cfl8rC2D/LMw=
-github.com/fyne-io/mobile v0.0.0-20191204153723-f14fb9562406/go.mod h1:HhyeqDET1gM7/EpVmvg/NWEaljaiP7HvoEldAUaoxOw=
+github.com/fyne-io/mobile v0.0.0-20200218100723-2a24c20a57c6 h1:Fk8kH4jY7e/hFzJALd24+Dp8ECJ6XTtpPjAX1Bv+WBU=
+github.com/fyne-io/mobile v0.0.0-20200218100723-2a24c20a57c6/go.mod h1:HhyeqDET1gM7/EpVmvg/NWEaljaiP7HvoEldAUaoxOw=
github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 h1:SCYMcCJ89LjRGwEa0tRluNRiMjZHalQZrVrvTbPh+qw=
github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72 h1:b+9H1GAsx5RsjvDFLoS5zkNBzIQMuVKUYQDmxU3N5XE=
diff --git a/vendor/fyne.io/fyne/internal/app/theme.go b/vendor/fyne.io/fyne/internal/app/theme.go
index 27f7306e..d803576a 100644
--- a/vendor/fyne.io/fyne/internal/app/theme.go
+++ b/vendor/fyne.io/fyne/internal/app/theme.go
@@ -8,6 +8,9 @@ import (
// ApplyThemeTo ensures that the specified canvasobject and all widgets and themeable objects will
// be updated for the current theme.
func ApplyThemeTo(content fyne.CanvasObject, canv fyne.Canvas) {
+ if content == nil {
+ return
+ }
if wid, ok := content.(fyne.Widget); ok {
for _, o := range cache.Renderer(wid).Objects() {
ApplyThemeTo(o, canv)
diff --git a/vendor/fyne.io/fyne/internal/driver/glfw/canvas.go b/vendor/fyne.io/fyne/internal/driver/glfw/canvas.go
index 04582090..641648ec 100644
--- a/vendor/fyne.io/fyne/internal/driver/glfw/canvas.go
+++ b/vendor/fyne.io/fyne/internal/driver/glfw/canvas.go
@@ -150,16 +150,20 @@ func (c *glCanvas) Focused() fyne.Focusable {
func (c *glCanvas) Resize(size fyne.Size) {
c.size = size
- c.content.Resize(c.contentSize(size))
- c.content.Move(c.contentPos())
if c.overlay != nil {
- if _, ok := c.overlay.(*widget.PopUp); ok {
- c.overlay.Resize(c.Overlay().MinSize())
+ if p, ok := c.overlay.(*widget.PopUp); ok {
+ // TODO: remove this when #707 is being addressed.
+ // “Notifies” the PopUp of the canvas size change.
+ c.overlay.Resize(p.Content.Size().Add(fyne.NewSize(theme.Padding()*2, theme.Padding()*2)))
} else {
c.overlay.Resize(size)
}
}
+
+ c.content.Resize(c.contentSize(size))
+ c.content.Move(c.contentPos())
+
if c.menu != nil {
c.menu.Refresh()
c.menu.Resize(fyne.NewSize(size.Width, c.menu.MinSize().Height))
@@ -270,7 +274,7 @@ func (c *glCanvas) ensureMinSize() bool {
windowNeedsMinSizeUpdate = true
size := obj.Size()
expectedSize := minSize.Union(size)
- if expectedSize != size {
+ if expectedSize != size && size != c.size {
objToLayout = nil
obj.Resize(expectedSize)
}
diff --git a/vendor/fyne.io/fyne/internal/driver/glfw/window.go b/vendor/fyne.io/fyne/internal/driver/glfw/window.go
index d1861208..b022a8d3 100644
--- a/vendor/fyne.io/fyne/internal/driver/glfw/window.go
+++ b/vendor/fyne.io/fyne/internal/driver/glfw/window.go
@@ -31,6 +31,7 @@ const (
var (
defaultCursor, entryCursor, hyperlinkCursor *glfw.Cursor
initOnce = &sync.Once{}
+ defaultTitle = "Fyne Application"
)
func initCursors() {
@@ -66,6 +67,7 @@ type window struct {
mouseButton desktop.MouseButton
mouseOver desktop.Hoverable
mouseClickTime time.Time
+ mouseLastClick fyne.CanvasObject
mousePressed fyne.Tappable
onClosed func()
@@ -246,6 +248,9 @@ func (w *window) fitContent() {
w.viewport.SetSize(w.width, w.height)
}
if w.fixedSize {
+ w.width = internal.ScaleInt(w.canvas, w.Canvas().Size().Width)
+ w.height = internal.ScaleInt(w.canvas, w.Canvas().Size().Height)
+
w.viewport.SetSizeLimits(w.width, w.height, w.width, w.height)
} else {
w.viewport.SetSizeLimits(minWidth, minHeight, glfw.DontCare, glfw.DontCare)
@@ -635,13 +640,14 @@ func (w *window) mouseClicked(viewport *glfw.Window, btn glfw.MouseButton, actio
if action == glfw.Release && button == desktop.LeftMouseButton {
now := time.Now()
// we can safely subtract the first "zero" time as it'll be much larger than doubleClickDelay
- if now.Sub(w.mouseClickTime).Nanoseconds()/1e6 <= doubleClickDelay {
+ if now.Sub(w.mouseClickTime).Nanoseconds()/1e6 <= doubleClickDelay && w.mouseLastClick == co {
if wid, ok := co.(fyne.DoubleTappable); ok {
doubleTapped = true
w.queueEvent(func() { wid.DoubleTapped(ev) })
}
}
w.mouseClickTime = now
+ w.mouseLastClick = co
}
// Prevent Tapped from triggering if DoubleTapped has been sent
@@ -882,7 +888,7 @@ func (w *window) keyPressed(viewport *glfw.Window, key glfw.Key, scancode int, a
shortcut = &fyne.ShortcutPaste{
Clipboard: w.Clipboard(),
}
- case fyne.KeyC:
+ case fyne.KeyC, fyne.KeyInsert:
// detect copy shortcut
shortcut = &fyne.ShortcutCopy{
Clipboard: w.Clipboard(),
@@ -897,6 +903,22 @@ func (w *window) keyPressed(viewport *glfw.Window, key glfw.Key, scancode int, a
shortcut = &fyne.ShortcutSelectAll{}
}
}
+
+ if keyDesktopModifier == desktop.ShiftModifier {
+ switch keyName {
+ case fyne.KeyInsert:
+ // detect paste shortcut
+ shortcut = &fyne.ShortcutPaste{
+ Clipboard: w.Clipboard(),
+ }
+ case fyne.KeyDelete:
+ // detect cut shortcut
+ shortcut = &fyne.ShortcutCut{
+ Clipboard: w.Clipboard(),
+ }
+ }
+ }
+
if shortcut == nil && keyDesktopModifier != 0 && keyDesktopModifier != desktop.ShiftModifier {
shortcut = &desktop.CustomShortcut{
KeyName: keyName,
@@ -1031,6 +1053,9 @@ func (d *gLDriver) CreateWindow(title string) fyne.Window {
func (d *gLDriver) createWindow(title string, decorate bool) fyne.Window {
var ret *window
+ if title == "" {
+ title = defaultTitle
+ }
runOnMain(func() {
initOnce.Do(d.initGLFW)
diff --git a/vendor/fyne.io/fyne/internal/driver/gomobile/canvas_android.go b/vendor/fyne.io/fyne/internal/driver/gomobile/canvas_android.go
index da8bec1e..d6b136b4 100644
--- a/vendor/fyne.io/fyne/internal/driver/gomobile/canvas_android.go
+++ b/vendor/fyne.io/fyne/internal/driver/gomobile/canvas_android.go
@@ -9,5 +9,14 @@ func devicePadding() (topLeft, bottomRight fyne.Size) {
}
func (*device) SystemScale() float32 {
- return 3 // TODO detect device DPI and pick from 1, 1.5, 2, 3 and 4
+ if currentDPI >= 600 {
+ return 4
+ } else if currentDPI >= 405 {
+ return 3
+ } else if currentDPI >= 270 {
+ return 2
+ } else if currentDPI >= 180 {
+ return 1.5
+ }
+ return 1
}
diff --git a/vendor/fyne.io/fyne/internal/driver/gomobile/canvas_ios.go b/vendor/fyne.io/fyne/internal/driver/gomobile/canvas_ios.go
index e1d349b1..262b7c06 100644
--- a/vendor/fyne.io/fyne/internal/driver/gomobile/canvas_ios.go
+++ b/vendor/fyne.io/fyne/internal/driver/gomobile/canvas_ios.go
@@ -15,5 +15,10 @@ func devicePadding() (topLeft, bottomRight fyne.Size) {
}
func (*device) SystemScale() float32 {
- return 3 // TODO return 2 for < iPhone X, Xs but 3 for Plus/Max size
+ if currentDPI >= 450 {
+ return 3
+ } else if currentDPI >= 340 {
+ return 2.5
+ }
+ return 2
}
diff --git a/vendor/fyne.io/fyne/internal/driver/gomobile/device.go b/vendor/fyne.io/fyne/internal/driver/gomobile/device.go
index f33d776d..4f920038 100644
--- a/vendor/fyne.io/fyne/internal/driver/gomobile/device.go
+++ b/vendor/fyne.io/fyne/internal/driver/gomobile/device.go
@@ -9,7 +9,10 @@ import (
type device struct {
}
-var currentOrientation size.Orientation
+var (
+ currentOrientation size.Orientation
+ currentDPI float32
+)
// Declare conformity with Device
var _ fyne.Device = (*device)(nil)
diff --git a/vendor/fyne.io/fyne/internal/driver/gomobile/driver.go b/vendor/fyne.io/fyne/internal/driver/gomobile/driver.go
index 5e518e74..781564c2 100644
--- a/vendor/fyne.io/fyne/internal/driver/gomobile/driver.go
+++ b/vendor/fyne.io/fyne/internal/driver/gomobile/driver.go
@@ -27,7 +27,6 @@ const tapSecondaryDelay = 300 * time.Millisecond
type mobileDriver struct {
app app.App
- quit bool
glctx gl.Context
windows []fyne.Window
@@ -72,7 +71,7 @@ func (d *mobileDriver) CanvasForObject(fyne.CanvasObject) fyne.Canvas {
return nil
}
- // TODO don't just assume it refers to the topmost window
+ // TODO figure out how we handle multiple windows...
return d.currentWindow().Canvas()
}
@@ -92,12 +91,7 @@ func (d *mobileDriver) AbsolutePositionForObject(co fyne.CanvasObject) fyne.Posi
}
func (d *mobileDriver) Quit() {
- if d.app == nil {
- return
- }
-
- // TODO should this be disabled for iOS?
- d.quit = true
+ // Android and iOS guidelines say this should not be allowed!
}
func (d *mobileDriver) Run() {
@@ -118,16 +112,25 @@ func (d *mobileDriver) Run() {
case lifecycle.CrossOn:
d.glctx, _ = e.DrawContext.(gl.Context)
d.onStart()
+
+ // this is a fix for some android phone to prevent the app from being drawn as a blank screen after being pushed in the background
+ canvas.Content().Refresh()
+
a.Send(paint.Event{})
case lifecycle.CrossOff:
d.onStop()
d.glctx = nil
}
- if e.Crosses(lifecycle.StageAlive) == lifecycle.CrossOff {
- d.quit = true
- }
case size.Event:
+ if e.WidthPx <= 0 {
+ continue
+ }
currentSize = e
+ currentOrientation = e.Orientation
+ currentDPI = e.PixelsPerPt * 72
+ canvas.SetScale(0) // value is ignored
+ // make sure that we paint on the next frame
+ canvas.Content().Refresh()
case paint.Event:
if d.glctx == nil || e.External {
continue
@@ -165,10 +168,6 @@ func (d *mobileDriver) Run() {
d.typeUpCanvas(canvas, e.Rune, e.Code)
}
}
-
- if d.quit {
- break
- }
}
})
}
@@ -181,7 +180,6 @@ func (d *mobileDriver) onStop() {
func (d *mobileDriver) paintWindow(window fyne.Window, sz size.Event) {
canvas := window.Canvas().(*mobileCanvas)
- currentOrientation = sz.Orientation
r, g, b, a := theme.BackgroundColor().RGBA()
max16bit := float32(255 * 255)
diff --git a/vendor/fyne.io/fyne/theme/bundled-icons.go b/vendor/fyne.io/fyne/theme/bundled-icons.go
index 87016ca5..44c916b7 100644
--- a/vendor/fyne.io/fyne/theme/bundled-icons.go
+++ b/vendor/fyne.io/fyne/theme/bundled-icons.go
@@ -140,7 +140,7 @@ var foldernewIconRes = &fyne.StaticResource{
var folderopenIconRes = &fyne.StaticResource{
StaticName: "folder-open.svg",
StaticContent: []byte{
- 60, 115, 118, 103, 32, 120, 109, 108, 110, 115, 61, 34, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46, 119, 51, 46, 111, 114, 103, 47, 50, 48, 48, 48, 47, 115, 118, 103, 34, 32, 119, 105, 100, 116, 104, 61, 34, 50, 52, 34, 32, 104, 101, 105, 103, 104, 116, 61, 34, 50, 52, 34, 32, 118, 105, 101, 119, 66, 111, 120, 61, 34, 48, 32, 48, 32, 50, 52, 32, 50, 52, 34, 62, 60, 112, 97, 116, 104, 32, 100, 61, 34, 77, 48, 32, 48, 104, 50, 52, 118, 50, 52, 72, 48, 122, 34, 32, 102, 105, 108, 108, 61, 34, 110, 111, 110, 101, 34, 47, 62, 60, 112, 97, 116, 104, 32, 100, 61, 34, 77, 50, 48, 32, 54, 104, 45, 56, 108, 45, 50, 45, 50, 72, 52, 99, 45, 49, 46, 49, 32, 48, 45, 49, 46, 57, 57, 46, 57, 45, 49, 46, 57, 57, 32, 50, 76, 50, 32, 49, 56, 99, 48, 32, 49, 46, 49, 46, 57, 32, 50, 32, 50, 32, 50, 104, 49, 54, 99, 49, 46, 49, 32, 48, 32, 50, 45, 46, 57, 32, 50, 45, 50, 86, 56, 99, 48, 45, 49, 46, 49, 45, 46, 57, 45, 50, 45, 50, 45, 50, 122, 109, 48, 32, 49, 50, 72, 52, 86, 56, 104, 49, 54, 118, 49, 48, 122, 34, 47, 62, 60, 47, 115, 118, 103, 62}}
+ 60, 115, 118, 103, 32, 120, 109, 108, 110, 115, 61, 34, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46, 119, 51, 46, 111, 114, 103, 47, 50, 48, 48, 48, 47, 115, 118, 103, 34, 32, 119, 105, 100, 116, 104, 61, 34, 50, 52, 34, 32, 104, 101, 105, 103, 104, 116, 61, 34, 50, 52, 34, 32, 118, 105, 101, 119, 66, 111, 120, 61, 34, 48, 32, 48, 32, 50, 52, 32, 50, 52, 34, 62, 60, 112, 97, 116, 104, 32, 100, 61, 34, 77, 48, 32, 48, 104, 50, 52, 118, 50, 52, 72, 48, 122, 34, 32, 102, 105, 108, 108, 61, 34, 110, 111, 110, 101, 34, 47, 62, 60, 112, 97, 116, 104, 32, 100, 61, 34, 77, 49, 57, 44, 50, 48, 72, 52, 67, 50, 46, 56, 57, 44, 50, 48, 32, 50, 44, 49, 57, 46, 49, 32, 50, 44, 49, 56, 86, 54, 67, 50, 44, 52, 46, 56, 57, 32, 50, 46, 56, 57, 44, 52, 32, 52, 44, 52, 72, 49, 48, 76, 49, 50, 44, 54, 72, 49, 57, 65, 50, 44, 50, 32, 48, 32, 48, 44, 49, 32, 50, 49, 44, 56, 72, 50, 49, 76, 52, 44, 56, 86, 49, 56, 76, 54, 46, 49, 52, 44, 49, 48, 72, 50, 51, 46, 50, 49, 76, 50, 48, 46, 57, 51, 44, 49, 56, 46, 53, 67, 50, 48, 46, 55, 44, 49, 57, 46, 51, 55, 32, 49, 57, 46, 57, 50, 44, 50, 48, 32, 49, 57, 44, 50, 48, 90, 34, 47, 62, 60, 47, 115, 118, 103, 62}}
var helpIconRes = &fyne.StaticResource{
StaticName: "help.svg",
StaticContent: []byte{
diff --git a/vendor/fyne.io/fyne/theme/svg.go b/vendor/fyne.io/fyne/theme/svg.go
index 0aacb6e8..6d6a9db4 100644
--- a/vendor/fyne.io/fyne/theme/svg.go
+++ b/vendor/fyne.io/fyne/theme/svg.go
@@ -24,40 +24,66 @@ type svg struct {
}
type pathObj struct {
- XMLName xml.Name `xml:"path"`
- Fill string `xml:"fill,attr"`
- D string `xml:"d,attr"`
+ XMLName xml.Name `xml:"path"`
+ Fill string `xml:"fill,attr,omitempty"`
+ Stroke string `xml:"stroke,attr,omitempty"`
+ StrokeWidth string `xml:"stroke-width,attr,omitempty"`
+ StrokeLineCap string `xml:"stroke-linecap,attr,omitempty"`
+ StrokeLineJoin string `xml:"stroke-linejoin,attr,omitempty"`
+ StrokeDashArray string `xml:"stroke-dasharray,attr,omitempty"`
+ D string `xml:"d,attr"`
}
type rectObj struct {
- XMLName xml.Name `xml:"rect"`
- Fill string `xml:"fill,attr"`
- X string `xml:"x,attr"`
- Y string `xml:"y,attr"`
- Width string `xml:"width,attr"`
- Height string `xml:"height,attr"`
+ XMLName xml.Name `xml:"rect"`
+ Fill string `xml:"fill,attr,omitempty"`
+ Stroke string `xml:"stroke,attr,omitempty"`
+ StrokeWidth string `xml:"stroke-width,attr,omitempty"`
+ StrokeLineCap string `xml:"stroke-linecap,attr,omitempty"`
+ StrokeLineJoin string `xml:"stroke-linejoin,attr,omitempty"`
+ StrokeDashArray string `xml:"stroke-dasharray,attr,omitempty"`
+ X string `xml:"x,attr"`
+ Y string `xml:"y,attr"`
+ Width string `xml:"width,attr"`
+ Height string `xml:"height,attr"`
}
type circleObj struct {
- XMLName xml.Name `xml:"circle"`
- Fill string `xml:"fill,attr"`
- CX string `xml:"cx,attr"`
- CY string `xml:"cy,attr"`
- R string `xml:"r,attr"`
+ XMLName xml.Name `xml:"circle"`
+ Fill string `xml:"fill,attr,omitempty"`
+ Stroke string `xml:"stroke,attr,omitempty"`
+ StrokeWidth string `xml:"stroke-width,attr,omitempty"`
+ StrokeLineCap string `xml:"stroke-linecap,attr,omitempty"`
+ StrokeLineJoin string `xml:"stroke-linejoin,attr,omitempty"`
+ StrokeDashArray string `xml:"stroke-dasharray,attr,omitempty"`
+ CX string `xml:"cx,attr"`
+ CY string `xml:"cy,attr"`
+ R string `xml:"r,attr"`
}
type polygonObj struct {
- XMLName xml.Name `xml:"polygon"`
- Fill string `xml:"fill,attr"`
- Points string `xml:"points,attr"`
+ XMLName xml.Name `xml:"polygon"`
+ Fill string `xml:"fill,attr,omitempty"`
+ Stroke string `xml:"stroke,attr,omitempty"`
+ StrokeWidth string `xml:"stroke-width,attr,omitempty"`
+ StrokeLineCap string `xml:"stroke-linecap,attr,omitempty"`
+ StrokeLineJoin string `xml:"stroke-linejoin,attr,omitempty"`
+ StrokeDashArray string `xml:"stroke-dasharray,attr,omitempty"`
+ Points string `xml:"points,attr"`
}
type objGroup struct {
- XMLName xml.Name `xml:"g"`
- ID string `xml:"id,attr"`
- Paths []*pathObj `xml:"path"`
- Rects []*rectObj `xml:"rect"`
- Polygons []*polygonObj `xml:"polygon"`
+ XMLName xml.Name `xml:"g"`
+ ID string `xml:"id,attr,omitempty"`
+ Fill string `xml:"fill,attr,omitempty"`
+ Stroke string `xml:"stroke,attr,omitempty"`
+ StrokeWidth string `xml:"stroke-width,attr,omitempty"`
+ StrokeLineCap string `xml:"stroke-linecap,attr,omitempty"`
+ StrokeLineJoin string `xml:"stroke-linejoin,attr,omitempty"`
+ StrokeDashArray string `xml:"stroke-dasharray,attr,omitempty"`
+ Paths []*pathObj `xml:"path"`
+ Rects []*rectObj `xml:"rect"`
+ Polygons []*polygonObj `xml:"polygon"`
}
func replacePathsFill(paths []*pathObj, hexColor string) {
diff --git a/vendor/fyne.io/fyne/widget/entry.go b/vendor/fyne.io/fyne/widget/entry.go
index 664a6648..82586826 100644
--- a/vendor/fyne.io/fyne/widget/entry.go
+++ b/vendor/fyne.io/fyne/widget/entry.go
@@ -10,7 +10,6 @@ import (
"fyne.io/fyne"
"fyne.io/fyne/canvas"
"fyne.io/fyne/driver/desktop"
- "fyne.io/fyne/internal/cache"
"fyne.io/fyne/theme"
)
@@ -152,14 +151,14 @@ func (e *entryRenderer) Layout(size fyne.Size) {
e.line.Resize(fyne.NewSize(size.Width, theme.Padding()))
e.line.Move(fyne.NewPos(0, size.Height-theme.Padding()))
- revealIconSize := fyne.NewSize(0, 0)
- if e.entry.passwordRevealer != nil {
- revealIconSize = fyne.NewSize(theme.IconInlineSize(), theme.IconInlineSize())
- e.entry.passwordRevealer.Resize(revealIconSize)
- e.entry.passwordRevealer.Move(fyne.NewPos(size.Width-revealIconSize.Width-theme.Padding(), theme.Padding()*2))
+ actionIconSize := fyne.NewSize(0, 0)
+ if e.entry.ActionItem != nil {
+ actionIconSize = fyne.NewSize(theme.IconInlineSize(), theme.IconInlineSize())
+ e.entry.ActionItem.Resize(actionIconSize)
+ e.entry.ActionItem.Move(fyne.NewPos(size.Width-actionIconSize.Width-theme.Padding(), theme.Padding()*2))
}
- entrySize := size.Subtract(fyne.NewSize(theme.Padding()*2-revealIconSize.Width, theme.Padding()*2))
+ entrySize := size.Subtract(fyne.NewSize(theme.Padding()*2-actionIconSize.Width, theme.Padding()*2))
e.entry.text.Resize(entrySize)
e.entry.text.Move(fyne.NewPos(theme.Padding(), theme.Padding()))
@@ -201,8 +200,8 @@ func (e *entryRenderer) Refresh() {
}
e.entry.text.Refresh()
- if e.entry.passwordRevealer != nil {
- e.entry.passwordRevealer.Refresh()
+ if e.entry.ActionItem != nil {
+ e.entry.ActionItem.Refresh()
}
canvas.Refresh(e.entry.super())
}
@@ -216,17 +215,12 @@ func (e *entryRenderer) Objects() []fyne.CanvasObject {
}
func (e *entryRenderer) Destroy() {
- if e.entry.popUp != nil {
- c := fyne.CurrentApp().Driver().CanvasForObject(e.entry.super())
- c.SetOverlay(nil)
- cache.Renderer(e.entry.popUp).Destroy()
- e.entry.popUp = nil
- }
}
// Declare conformity with interfaces
var _ fyne.Draggable = (*Entry)(nil)
var _ fyne.Tappable = (*Entry)(nil)
+var _ fyne.Widget = (*Entry)(nil)
var _ desktop.Mouseable = (*Entry)(nil)
var _ desktop.Keyable = (*Entry)(nil)
@@ -262,8 +256,8 @@ type Entry struct {
popUp *PopUp
// TODO: Add OnSelectChanged
- // passwordRevealer represents the passwordRevealer widget
- passwordRevealer *passwordRevealer
+ // ActionItem is a small item which is displayed at the outer right of the entry (like a password revealer)
+ ActionItem fyne.CanvasObject
}
// SetText manually sets the text of the Entry to the given text value.
@@ -319,6 +313,22 @@ func (e *Entry) Disable() { // TODO remove this override after ReadOnly is remov
e.DisableableWidget.Disable()
}
+// Hide satisfies the fyne.CanvasObject interface.
+func (e *Entry) Hide() {
+ if e.popUp != nil {
+ e.popUp.Hide()
+ }
+ e.DisableableWidget.Hide()
+}
+
+// Show satisfies the fyne.CanvasObject interface.
+func (e *Entry) Show() {
+ if e.popUp != nil {
+ e.popUp.Show()
+ }
+ e.DisableableWidget.Show()
+}
+
// updateText updates the internal text to the given value
func (e *Entry) updateText(text string) {
changed := e.Text != text
@@ -453,9 +463,9 @@ func (e *Entry) Tapped(ev *fyne.PointEvent) {
}
// copyToClipboard copies the current selection to a given clipboard and then removes the selected text.
-// This does nothing if it is a password entry.
+// This does nothing if it is a concealed entry.
func (e *Entry) cutToClipboard(clipboard fyne.Clipboard) {
- if !e.selecting || e.password() {
+ if !e.selecting || e.concealed() {
return
}
@@ -464,9 +474,9 @@ func (e *Entry) cutToClipboard(clipboard fyne.Clipboard) {
}
// copyToClipboard copies the current selection to a given clipboard.
-// This does nothing if it is a password entry.
+// This does nothing if it is a concealed entry.
func (e *Entry) copyToClipboard(clipboard fyne.Clipboard) {
- if !e.selecting || e.password() {
+ if !e.selecting || e.concealed() {
return
}
@@ -543,13 +553,13 @@ func (e *Entry) TappedSecondary(pe *fyne.PointEvent) {
popUpPos := entryPos.Add(fyne.NewPos(pe.Position.X, pe.Position.Y))
c := fyne.CurrentApp().Driver().CanvasForObject(super)
- if e.Disabled() && e.password() {
- return // no popup options for a disabled password field
+ if e.Disabled() && e.concealed() {
+ return // no popup options for a disabled concealed field
}
if e.Disabled() {
e.popUp = NewPopUpMenuAtPosition(fyne.NewMenu("", copyItem, selectAllItem), c, popUpPos)
- } else if e.password() {
+ } else if e.concealed() {
e.popUp = NewPopUpMenuAtPosition(fyne.NewMenu("", pasteItem, selectAllItem), c, popUpPos)
} else {
e.popUp = NewPopUpMenuAtPosition(fyne.NewMenu("", cutItem, copyItem, pasteItem, selectAllItem), c, popUpPos)
@@ -958,6 +968,11 @@ func (e *Entry) TypedKey(key *fyne.KeyEvent) {
}
e.updateText(provider.String())
+
+ if e.CursorRow == e.selectRow && e.CursorColumn == e.selectColumn {
+ e.selecting = false
+ }
+
e.Refresh()
}
@@ -1006,8 +1021,8 @@ func (e *Entry) textColor() color.Color {
return theme.TextColor()
}
-// password tells the rendering textProvider if we are a password field
-func (e *Entry) password() bool {
+// concealed tells the rendering textProvider if we are a concealed field
+func (e *Entry) concealed() bool {
return e.Password
}
@@ -1035,9 +1050,9 @@ func (p *placeholderPresenter) textColor() color.Color {
return theme.PlaceHolderColor()
}
-// password tells the rendering textProvider if we are a password field
+// concealed tells the rendering textProvider if we are a concealed field
// placeholder text is not obfuscated, returning false
-func (p *placeholderPresenter) password() bool {
+func (p *placeholderPresenter) concealed() bool {
return false
}
@@ -1051,7 +1066,7 @@ func (e *Entry) MinSize() fyne.Size {
e.ExtendBaseWidget(e)
min := e.BaseWidget.MinSize()
- if e.passwordRevealer != nil {
+ if e.ActionItem != nil {
min = min.Add(fyne.NewSize(theme.IconInlineSize()+theme.Padding(), 0))
}
@@ -1068,20 +1083,14 @@ func (e *Entry) CreateRenderer() fyne.WidgetRenderer {
objects := []fyne.CanvasObject{line, e.placeholderProvider(), e.textProvider(), cursor}
- if e.Password && e.passwordRevealer == nil {
+ if e.Password && e.ActionItem == nil {
// An entry widget has been created via struct setting manually
// the Password field to true. Going to enable the password revealer.
- pr := &passwordRevealer{
- icon: canvas.NewImageFromResource(theme.VisibilityOffIcon()),
- entry: e,
- }
- pr.ExtendBaseWidget(pr)
-
- e.passwordRevealer = pr
+ e.ActionItem = newPasswordRevealer(e)
}
- if e.passwordRevealer != nil {
- objects = append(objects, e.passwordRevealer)
+ if e.ActionItem != nil {
+ objects = append(objects, e.ActionItem)
}
return &entryRenderer{line, cursor, []fyne.CanvasObject{}, objects, e}
}
@@ -1132,14 +1141,7 @@ func NewMultiLineEntry() *Entry {
func NewPasswordEntry() *Entry {
e := &Entry{Password: true}
e.ExtendBaseWidget(e)
-
- pr := &passwordRevealer{
- icon: canvas.NewImageFromResource(theme.VisibilityOffIcon()),
- entry: e,
- }
- pr.ExtendBaseWidget(pr)
-
- e.passwordRevealer = pr
+ e.ActionItem = newPasswordRevealer(e)
return e
}
@@ -1201,3 +1203,12 @@ func (pr *passwordRevealer) Tapped(*fyne.PointEvent) {
func (pr *passwordRevealer) TappedSecondary(*fyne.PointEvent) {
}
+
+func newPasswordRevealer(e *Entry) *passwordRevealer {
+ pr := &passwordRevealer{
+ icon: canvas.NewImageFromResource(theme.VisibilityOffIcon()),
+ entry: e,
+ }
+ pr.ExtendBaseWidget(pr)
+ return pr
+}
diff --git a/vendor/fyne.io/fyne/widget/form.go b/vendor/fyne.io/fyne/widget/form.go
index b7175d98..de575892 100644
--- a/vendor/fyne.io/fyne/widget/form.go
+++ b/vendor/fyne.io/fyne/widget/form.go
@@ -2,6 +2,7 @@ package widget
import (
"fyne.io/fyne"
+ "fyne.io/fyne/internal/cache"
"fyne.io/fyne/layout"
"fyne.io/fyne/theme"
)
@@ -53,14 +54,16 @@ func (f *Form) Append(text string, widget fyne.CanvasObject) {
// AppendItem adds the specified row to the end of the Form
func (f *Form) AppendItem(item *FormItem) {
- // ensure we have a renderer set up
- Renderer(f)
+ f.ExtendBaseWidget(f) // could be called before render
+
+ // ensure we have a renderer set up (that creates itemGrid)...
+ cache.Renderer(f.super())
f.Items = append(f.Items, item)
f.itemGrid.AddObject(f.createLabel(item.Text))
f.itemGrid.AddObject(item.Widget)
- Refresh(f)
+ f.Refresh()
}
// MinSize returns the size that this widget should not shrink below
@@ -79,7 +82,7 @@ func (f *Form) CreateRenderer() fyne.WidgetRenderer {
}
if f.OnCancel == nil && f.OnSubmit == nil {
- return Renderer(NewVBox(f.itemGrid))
+ return cache.Renderer(NewVBox(f.itemGrid))
}
buttons := NewHBox(layout.NewSpacer())
@@ -99,14 +102,14 @@ func (f *Form) CreateRenderer() fyne.WidgetRenderer {
submitButton.Style = PrimaryButton
buttons.Append(submitButton)
}
- return Renderer(NewVBox(f.itemGrid, buttons))
+ return cache.Renderer(NewVBox(f.itemGrid, buttons))
}
// NewForm creates a new form widget with the specified rows of form items
// and (if any of them should be shown) a form controls row at the bottom
func NewForm(items ...*FormItem) *Form {
form := &Form{BaseWidget: BaseWidget{}, Items: items}
+ form.ExtendBaseWidget(form)
- Renderer(form).Layout(form.MinSize())
return form
}
diff --git a/vendor/fyne.io/fyne/widget/hyperlink.go b/vendor/fyne.io/fyne/widget/hyperlink.go
index 7f048858..f4d1d7a5 100644
--- a/vendor/fyne.io/fyne/widget/hyperlink.go
+++ b/vendor/fyne.io/fyne/widget/hyperlink.go
@@ -71,8 +71,8 @@ func (hl *Hyperlink) textColor() color.Color {
return theme.HyperlinkColor()
}
-// password tells the rendering textProvider if we are a password field
-func (hl *Hyperlink) password() bool {
+// concealed tells the rendering textProvider if we are a concealed field
+func (hl *Hyperlink) concealed() bool {
return false
}
diff --git a/vendor/fyne.io/fyne/widget/label.go b/vendor/fyne.io/fyne/widget/label.go
index 8952e761..4a3f149e 100644
--- a/vendor/fyne.io/fyne/widget/label.go
+++ b/vendor/fyne.io/fyne/widget/label.go
@@ -31,6 +31,15 @@ func NewLabelWithStyle(text string, alignment fyne.TextAlign, style fyne.TextSty
return l
}
+// Refresh checks if the text content should be updated then refreshes the graphical context
+func (l *Label) Refresh() {
+ if l.Text != string(l.buffer) {
+ l.textProvider.SetText(l.Text)
+ }
+
+ l.BaseWidget.Refresh()
+}
+
// SetText sets the text of the label
func (l *Label) SetText(text string) {
l.Text = text
@@ -52,8 +61,8 @@ func (l *Label) textColor() color.Color {
return theme.TextColor()
}
-// password tells the rendering textProvider if we are a password field
-func (l *Label) password() bool {
+// concealed tells the rendering textProvider if we are a concealed field
+func (l *Label) concealed() bool {
return false
}
diff --git a/vendor/fyne.io/fyne/widget/popup.go b/vendor/fyne.io/fyne/widget/popup.go
index 5cfd7849..25f85aa6 100644
--- a/vendor/fyne.io/fyne/widget/popup.go
+++ b/vendor/fyne.io/fyne/widget/popup.go
@@ -5,7 +5,6 @@ import (
"fyne.io/fyne"
"fyne.io/fyne/canvas"
- "fyne.io/fyne/internal/cache"
"fyne.io/fyne/layout"
"fyne.io/fyne/theme"
)
@@ -19,7 +18,9 @@ type PopUp struct {
Content fyne.CanvasObject
Canvas fyne.Canvas
- modal bool
+ innerPos fyne.Position
+ innerSize fyne.Size
+ modal bool
}
// Hide this widget, if it was previously visible
@@ -34,32 +35,18 @@ func (p *PopUp) Move(pos fyne.Position) {
if p.modal {
return
}
-
- innerSize := p.Content.MinSize().Union(p.Content.Size())
- if pos.X+innerSize.Width > p.Canvas.Size().Width-theme.Padding()*2 {
- pos.X = p.Canvas.Size().Width - innerSize.Width - theme.Padding()*2
- if pos.X < 0 {
- pos.X = 0 // TODO here we may need a scroller as it's wider than our canvas
- }
- }
-
- if pos.Y+innerSize.Height > p.Canvas.Size().Height-theme.Padding()*2 {
- pos.Y = p.Canvas.Size().Height - innerSize.Height - theme.Padding()*2
- if pos.Y < 0 {
- pos.Y = 0 // TODO here we may need a scroller as it's longer than our canvas
- }
- }
-
- p.Content.Move(pos.Add(fyne.NewPos(theme.Padding(), theme.Padding())))
+ p.innerPos = pos
p.Refresh()
- cache.Renderer(p).Layout(p.Size())
}
-// Resize sets a new size for a widget. Most PopUp widgets are shown at MinSize.
+// Resize satisfies the fyne.CanvasObject interface.
+// PopUps always have the size of their canvas.
+// However, Resize changes the size of the PopUp's content.
func (p *PopUp) Resize(size fyne.Size) {
+ p.innerSize = size
p.BaseWidget.Resize(p.Canvas.Size())
-
- p.Content.Resize(size.Subtract(fyne.NewSize(theme.Padding()*2, theme.Padding()*2)))
+ // The canvas size might not have changed and therefore the Resize won't trigger a layout.
+ // Until we have a widget.Relayout() or similar, the renderer's refresh will do the re-layout.
p.Refresh()
}
@@ -111,7 +98,7 @@ func NewPopUpAtPosition(content fyne.CanvasObject, canvas fyne.Canvas, pos fyne.
ret.ExtendBaseWidget(ret)
ret.Move(pos)
- ret.Resize(ret.Content.MinSize())
+ ret.Resize(ret.MinSize())
ret.Show()
return ret
}
@@ -139,16 +126,29 @@ type popUpRenderer struct {
}
func (r *popUpRenderer) Layout(_ fyne.Size) {
- pos := r.popUp.Content.Position().Subtract(fyne.NewPos(theme.Padding(), theme.Padding()))
- innerSize := r.popUp.Content.MinSize().Union(r.popUp.Content.Size())
- r.popUp.Content.Resize(innerSize)
- r.popUp.Content.Move(pos.Add(fyne.NewPos(theme.Padding(), theme.Padding())))
+ contentSize := r.popUp.innerSize.Subtract(fyne.NewSize(theme.Padding()*2, theme.Padding()*2))
+ r.popUp.Content.Resize(contentSize)
+
+ innerPos := r.popUp.innerPos
+ if innerPos.X+r.popUp.innerSize.Width > r.popUp.Canvas.Size().Width {
+ innerPos.X = r.popUp.Canvas.Size().Width - r.popUp.innerSize.Width
+ if innerPos.X < 0 {
+ innerPos.X = 0 // TODO here we may need a scroller as it's wider than our canvas
+ }
+ }
+ if innerPos.Y+r.popUp.innerSize.Height > r.popUp.Canvas.Size().Height {
+ innerPos.Y = r.popUp.Canvas.Size().Height - r.popUp.innerSize.Height
+ if innerPos.Y < 0 {
+ innerPos.Y = 0 // TODO here we may need a scroller as it's longer than our canvas
+ }
+ }
+ contentPos := innerPos.Add(fyne.NewPos(theme.Padding(), theme.Padding()))
+ r.popUp.Content.Move(contentPos)
- size := innerSize.Add(fyne.NewSize(theme.Padding()*2, theme.Padding()*2))
- r.bg.Resize(size)
- r.bg.Move(pos)
- r.shadow.Resize(size)
- r.shadow.Move(pos)
+ r.bg.Resize(r.popUp.innerSize)
+ r.bg.Move(innerPos)
+ r.shadow.Resize(r.popUp.innerSize)
+ r.shadow.Move(innerPos)
}
func (r *popUpRenderer) MinSize() fyne.Size {
@@ -157,6 +157,9 @@ func (r *popUpRenderer) MinSize() fyne.Size {
func (r *popUpRenderer) Refresh() {
r.bg.FillColor = theme.BackgroundColor()
+ if r.bg.Size() != r.popUp.innerSize || r.bg.Position() != r.popUp.innerPos {
+ r.Layout(r.popUp.Size())
+ }
}
func (r *popUpRenderer) BackgroundColor() color.Color {
@@ -190,6 +193,9 @@ func (r *modalPopUpRenderer) MinSize() fyne.Size {
func (r *modalPopUpRenderer) Refresh() {
r.bg.FillColor = theme.BackgroundColor()
+ if r.bg.Size() != r.popUp.innerSize {
+ r.Layout(r.popUp.Size())
+ }
}
func (r *modalPopUpRenderer) BackgroundColor() color.Color {
diff --git a/vendor/fyne.io/fyne/widget/select.go b/vendor/fyne.io/fyne/widget/select.go
index b801d220..4b57c968 100644
--- a/vendor/fyne.io/fyne/widget/select.go
+++ b/vendor/fyne.io/fyne/widget/select.go
@@ -6,7 +6,6 @@ import (
"fyne.io/fyne"
"fyne.io/fyne/canvas"
"fyne.io/fyne/driver/desktop"
- "fyne.io/fyne/internal/cache"
"fyne.io/fyne/theme"
)
@@ -87,12 +86,6 @@ func (s *selectRenderer) Objects() []fyne.CanvasObject {
}
func (s *selectRenderer) Destroy() {
- if s.combo.popUp != nil {
- c := fyne.CurrentApp().Driver().CanvasForObject(s.combo)
- c.SetOverlay(nil)
- cache.Renderer(s.combo.popUp).Destroy()
- s.combo.popUp = nil
- }
}
// Select widget has a list of options, with the current one shown, and triggers an event func when clicked
@@ -108,13 +101,31 @@ type Select struct {
popUp *PopUp
}
+var _ fyne.Widget = (*Select)(nil)
+
+// Hide satisfies the fyne.CanvasObject interface.
+func (s *Select) Hide() {
+ if s.popUp != nil {
+ s.popUp.Hide()
+ }
+ s.BaseWidget.Hide()
+}
+
+// Show satisfies the fyne.CanvasObject interface.
+func (s *Select) Show() {
+ if s.popUp != nil {
+ s.popUp.Show()
+ }
+ s.BaseWidget.Show()
+}
+
// Resize sets a new size for a widget.
// Note this should not be used if the widget is being managed by a Layout within a Container.
func (s *Select) Resize(size fyne.Size) {
s.BaseWidget.Resize(size)
if s.popUp != nil {
- s.popUp.Content.Resize(fyne.NewSize(size.Width, s.popUp.MinSize().Height))
+ s.popUp.Resize(fyne.NewSize(size.Width, s.popUp.MinSize().Height))
}
}
@@ -140,7 +151,7 @@ func (s *Select) Tapped(*fyne.PointEvent) {
popUpPos := buttonPos.Add(fyne.NewPos(0, s.Size().Height))
s.popUp = NewPopUpMenuAtPosition(fyne.NewMenu("", items...), c, popUpPos)
- s.popUp.Resize(fyne.NewSize(s.Size().Width, s.popUp.Content.MinSize().Height))
+ s.popUp.Resize(fyne.NewSize(s.Size().Width, s.popUp.MinSize().Height))
}
// TappedSecondary is called when a secondary pointer tapped event is captured
diff --git a/vendor/fyne.io/fyne/widget/text.go b/vendor/fyne.io/fyne/widget/text.go
index 72b7ddc7..95a99053 100644
--- a/vendor/fyne.io/fyne/widget/text.go
+++ b/vendor/fyne.io/fyne/widget/text.go
@@ -19,7 +19,7 @@ type textPresenter interface {
textStyle() fyne.TextStyle
textColor() color.Color
- password() bool
+ concealed() bool
object() fyne.Widget
}
@@ -207,7 +207,7 @@ func (t *textHandler) rowLength(row int) int {
// CharMinSize returns the average char size to use for internal computation
func (t *textProvider) charMinSize() fyne.Size {
defaultChar := "M"
- if t.presenter.password() {
+ if t.presenter.concealed() {
defaultChar = passwordChar
}
return textMinSize(defaultChar, theme.TextSize(), t.presenter.textStyle())
@@ -225,7 +225,7 @@ func (t *textProvider) lineSizeToColumn(col, row int) (size fyne.Size) {
}
measureText := string(line[0:col])
- if t.presenter.password() {
+ if t.presenter.concealed() {
measureText = strings.Repeat(passwordChar, col)
}
@@ -293,7 +293,7 @@ func (r *textRenderer) Refresh() {
for ; index < r.provider.rows(); index++ {
var line string
row := r.provider.row(index)
- if r.provider.presenter.password() {
+ if r.provider.presenter.concealed() {
line = strings.Repeat(passwordChar, len(row))
} else {
line = string(row)
diff --git a/vendor/fyne.io/fyne/widget/textgrid.go b/vendor/fyne.io/fyne/widget/textgrid.go
index 65ae0a70..e6b37da5 100644
--- a/vendor/fyne.io/fyne/widget/textgrid.go
+++ b/vendor/fyne.io/fyne/widget/textgrid.go
@@ -17,11 +17,22 @@ const (
textAreaNewLineSymbol = '↵'
)
+// TextGridCell represents a single cell in a text grid.
+// It has a rune for the text content and a style associated with it.
+type TextGridCell struct {
+ Rune rune
+ TextColor color.Color
+}
+
+var (
+ whitespaceColor = theme.ButtonColor()
+)
+
// TextGrid is a monospaced grid of characters.
-// This is designed to be used by a text editor or advanced test presentation.
+// This is designed to be used by a text editor, code preview or terminal emulator.
type TextGrid struct {
BaseWidget
- Buffer [][]rune
+ Content [][]TextGridCell
LineNumbers bool
Whitespace bool
@@ -33,27 +44,40 @@ func (t *TextGrid) MinSize() fyne.Size {
return t.BaseWidget.MinSize()
}
+// Resize is called when this widget changes size. We should make sure that we refresh cells.
+func (t *TextGrid) Resize(size fyne.Size) {
+ t.BaseWidget.Resize(size)
+ t.Refresh()
+}
+
// SetText updates the buffer of this textgrid to contain the specified text.
// New lines and columns will be added as required. Lines are separated by '\n'.
+// The grid will use default text style and any previous content and style will be removed.
func (t *TextGrid) SetText(text string) {
rows := strings.Split(text, "\n")
- var buffer [][]rune
- for _, row := range rows {
- buffer = append(buffer, []rune(row))
+ var buffer [][]TextGridCell
+ for _, runes := range rows {
+ var row []TextGridCell
+ for _, r := range runes {
+ row = append(row, TextGridCell{Rune: r})
+ }
+ buffer = append(buffer, row)
}
- t.Buffer = buffer
+ t.Content = buffer
t.Refresh()
}
-// Text returns the contents of the buffer as a single string.
+// Text returns the contents of the buffer as a single string (with no style information).
// It reconstructs the lines by joining with a `\n` character.
func (t *TextGrid) Text() string {
ret := ""
- for i, row := range t.Buffer {
- ret += string(row)
+ for i, row := range t.Content {
+ for _, r := range row {
+ ret += string(r.Rune)
+ }
- if i < len(t.Buffer)-1 {
+ if i < len(t.Content)-1 {
ret += "\n"
}
}
@@ -61,26 +85,27 @@ func (t *TextGrid) Text() string {
return ret
}
-// Row returns the []rune content of a specified row. If the index is out of bounds it returns an empty slice.
-func (t *TextGrid) Row(row int) []rune {
- if row < 0 || row >= len(t.Buffer) {
- return []rune{}
+// Row returns the content of a specified row as a slice of TextGridCells.
+// If the index is out of bounds it returns an empty slice.
+func (t *TextGrid) Row(row int) []TextGridCell {
+ if row < 0 || row >= len(t.Content) {
+ return []TextGridCell{}
}
- return t.Buffer[row]
+ return t.Content[row]
}
-// SetRow updates the specified row of the grid's buffer using the specified content and then refreshes.
+// SetRow updates the specified row of the grid's contents using the specified cell content and style and then refreshes.
// If the row is beyond the end of the current buffer it will be expanded.
-func (t *TextGrid) SetRow(row int, content []rune) {
+func (t *TextGrid) SetRow(row int, content []TextGridCell) {
if row < 0 {
return
}
- for len(t.Buffer) <= row {
- t.Buffer = append(t.Buffer, []rune{})
+ for len(t.Content) <= row {
+ t.Content = append(t.Content, []TextGridCell{})
}
- t.Buffer[row] = content
+ t.Content[row] = content
t.Refresh()
}
@@ -88,7 +113,6 @@ func (t *TextGrid) SetRow(row int, content []rune) {
func (t *TextGrid) CreateRenderer() fyne.WidgetRenderer {
t.ExtendBaseWidget(t)
render := &textGridRender{text: t}
- render.update()
cell := canvas.NewText("M", color.White)
cell.TextStyle.Monospace = true
@@ -127,20 +151,20 @@ func (t *textGridRender) appendTextCell(str rune) {
t.objects = append(t.objects, text)
}
-func (t *textGridRender) setCellRune(str rune, pos int) {
+func (t *textGridRender) setCellRune(str rune, pos int, cellFG color.Color) {
text := t.objects[pos].(*canvas.Text)
- text.Text = string(str)
-
- if str == textAreaSpaceSymbol || str == textAreaTabSymbol || str == textAreaNewLineSymbol {
- text.Color = theme.PlaceHolderColor()
+ if str == 0 {
+ text.Text = " "
} else {
- text.Color = theme.TextColor()
+ text.Text = string(str)
}
-}
-func (t *textGridRender) update() {
- t.ensureGrid()
- t.refreshGrid()
+ fg := theme.TextColor()
+ if cellFG != nil {
+ fg = cellFG
+ }
+
+ text.Color = fg
}
func (t *textGridRender) ensureGrid() {
@@ -157,7 +181,7 @@ func (t *textGridRender) refreshGrid() {
line := 1
x := 0
- for rowIndex, row := range t.text.Buffer {
+ for rowIndex, row := range t.text.Content {
if rowIndex >= t.rows { // would be an overflow - bad
break
}
@@ -165,16 +189,16 @@ func (t *textGridRender) refreshGrid() {
if t.text.LineNumbers {
lineStr := []rune(fmt.Sprintf("%d", line))
for c := 0; c < len(lineStr); c++ {
- t.setCellRune(lineStr[c], x)
+ t.setCellRune(lineStr[c], x, whitespaceColor) // line numbers
i++
x++
}
for ; i < t.lineCountWidth(); i++ {
- t.setCellRune(' ', x)
+ t.setCellRune(' ', x, whitespaceColor) // padding space
x++
}
- t.setCellRune(' ', x)
+ t.setCellRune('|', x, whitespaceColor) // last space
i++
x++
}
@@ -182,27 +206,28 @@ func (t *textGridRender) refreshGrid() {
if i >= t.cols { // would be an overflow - bad
continue
}
- if t.text.Whitespace && r == ' ' {
- r = textAreaSpaceSymbol
+ if t.text.Whitespace && r.Rune == ' ' {
+ t.setCellRune(textAreaSpaceSymbol, x, whitespaceColor) // whitespace char
+ } else {
+ t.setCellRune(r.Rune, x, r.TextColor) // regular char
}
- t.setCellRune(r, x)
i++
x++
}
- if t.text.Whitespace && i < t.cols {
- t.setCellRune(textAreaNewLineSymbol, x)
+ if t.text.Whitespace && i < t.cols && rowIndex < len(t.text.Content)-1 {
+ t.setCellRune(textAreaNewLineSymbol, x, whitespaceColor) // newline
i++
x++
}
for ; i < t.cols; i++ {
- t.setCellRune(' ', x)
+ t.setCellRune(' ', x, nil) // blanks
x++
}
line++
}
for ; x < len(t.objects); x++ {
- t.setCellRune(' ', x)
+ t.setCellRune(' ', x, nil) // blank lines?
}
canvas.Refresh(t.text)
}
@@ -212,14 +237,21 @@ func (t *textGridRender) lineCountWidth() int {
}
func (t *textGridRender) updateGridSize(size fyne.Size) {
- bufRows := len(t.text.Buffer)
+ bufRows := len(t.text.Content)
bufCols := 0
- for _, row := range t.text.Buffer {
+ for _, row := range t.text.Content {
bufCols = int(math.Max(float64(bufCols), float64(len(row))))
}
sizeCols := int(math.Floor(float64(size.Width) / float64(t.cellSize.Width)))
sizeRows := int(math.Floor(float64(size.Height) / float64(t.cellSize.Height)))
+ if t.text.Whitespace {
+ bufCols++
+ }
+ if t.text.LineNumbers {
+ bufCols += t.lineCountWidth()
+ }
+
t.cols = int(math.Max(float64(sizeCols), float64(bufCols)))
t.rows = int(math.Max(float64(sizeRows), float64(bufRows)))
}
@@ -248,6 +280,7 @@ func (t *textGridRender) MinSize() fyne.Size {
}
func (t *textGridRender) Refresh() {
+ t.ensureGrid()
t.refreshGrid()
}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 52eaf08c..882f82cc 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -1,4 +1,4 @@
-# fyne.io/fyne v1.2.2-0.20200229144543-75524de963d0
+# fyne.io/fyne v1.2.4-0.20200302154258-a1e26ef951f6
fyne.io/fyne
fyne.io/fyne/app
fyne.io/fyne/canvas