diff --git a/README.md b/README.md index 2156d9a3..bad49749 100644 --- a/README.md +++ b/README.md @@ -9,4 +9,4 @@ A Linux terminal emulator using the Fyne toolkit. -screenshot +screenshot diff --git a/escape.go b/escape.go index 7322dbd8..270df850 100644 --- a/escape.go +++ b/escape.go @@ -1,11 +1,16 @@ package terminal import ( + "image/color" "log" "strconv" "strings" + + "fyne.io/fyne/theme" ) +var currentFG color.Color + func (t *Terminal) handleEscape(code string) { switch code { // exact matches case "H", ";H": @@ -16,6 +21,9 @@ func (t *Terminal) handleEscape(code string) { t.clearScreen() case "K": row := t.content.Row(t.cursorRow) + if t.cursorCol > len(row) { + return + } t.content.SetRow(t.cursorRow, row[:t.cursorCol]) default: // check mode (last letter) then match message := code[:len(code)-1] @@ -25,13 +33,46 @@ func (t *Terminal) handleEscape(code string) { row, _ := strconv.Atoi(parts[0]) col, _ := strconv.Atoi(parts[1]) - if row < len(t.content.Buffer) { + if row < len(t.content.Content) { t.cursorRow = row } line := t.content.Row(t.cursorRow) if col < len(line) { t.cursorCol = col } + case "m": + if message == "" || message == "0" { + currentFG = nil + return + } + + modes := strings.Split(message, ";") + for _, mode := range modes { + switch mode { + case "7": // reverse + currentFG = theme.BackgroundColor() + case "27": // reverse off + currentFG = nil + case "30": + currentFG = color.Black + case "31": + currentFG = &color.RGBA{255, 0, 0, 255} + case "32": + currentFG = &color.RGBA{0, 255, 0, 255} + case "33": + currentFG = &color.RGBA{255, 255, 0, 255} + case "34": + currentFG = &color.RGBA{0, 0, 255, 255} + case "35": + currentFG = &color.RGBA{255, 0, 255, 255} + case "36": + currentFG = &color.RGBA{0, 255, 255, 255} + case "37": + currentFG = color.White + default: + log.Println("Unsupported graphics mode", mode) + } + } default: log.Println("Unrecognised Escape:", code) } diff --git a/go.mod b/go.mod index 21c807bb..d5830181 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/fyne-io/terminal go 1.13 require ( - fyne.io/fyne v1.2.2-0.20200229144543-75524de963d0 + fyne.io/fyne v1.2.4-0.20200302154258-a1e26ef951f6 github.com/creack/pty v1.1.9 github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709 - golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d // indirect + golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 // indirect ) diff --git a/go.sum b/go.sum index 3b34f811..c7c06217 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,5 @@ -fyne.io/fyne v1.2.2-0.20200223223253-472c76c32bb7 h1:I5aBskVBj1ouMFU+SiQrmeFYX+4HREerixMlawiMgCo= -fyne.io/fyne v1.2.2-0.20200223223253-472c76c32bb7/go.mod h1:AwxWB9CEwG1B7XFmnj5zRW5jEI5aiyeXYe4Ve83L96c= -fyne.io/fyne v1.2.2-0.20200229144543-75524de963d0 h1:dXWcw/j9rE2vN8cgAkUo6RgPsfMfxPx+oUWfHLvREqs= -fyne.io/fyne v1.2.2-0.20200229144543-75524de963d0/go.mod h1:AwxWB9CEwG1B7XFmnj5zRW5jEI5aiyeXYe4Ve83L96c= +fyne.io/fyne v1.2.4-0.20200302154258-a1e26ef951f6 h1:xIllRcR1Wua8vCkqY1KvLsc1UfYNyWIlvRY6qjqpKXM= +fyne.io/fyne v1.2.4-0.20200302154258-a1e26ef951f6/go.mod h1:5nHgSQyRojVlrXE7qrNeRfVNXD8OUDaRt/Dudk5D0us= github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod h1:7uhhqiBaR4CpN0k9rMjOtjpcfGd6DG2m04zQxKnWQ0I= github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= @@ -34,8 +32,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709 h1:Ko2LQMrRU+Oy/+EDBwX7eZ2jp3C47eDBB8EIhKTun+I= github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d h1:1ZiEyfaQIg3Qh0EoqpwAakHVhecoE5wlSg5GjnafJGw= -golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8 h1:idBdZTd9UioThJp8KpM/rTSinK/ChZFBE43/WtIy8zg= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= @@ -47,7 +43,6 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2eP golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= diff --git a/output.go b/output.go index 5a54693d..0fb1dbf4 100644 --- a/output.go +++ b/output.go @@ -1,7 +1,10 @@ package terminal import ( + "image/color" "time" + + "fyne.io/fyne/widget" ) func (t *Terminal) handleOutput(buf []byte) { @@ -64,14 +67,23 @@ func (t *Terminal) handleOutput(buf []byte) { continue case '\n': row := t.content.Row(t.cursorRow) - row = append(row, []rune(out)...) + // TODO use styles here too + for i, r := range out { + i += t.cursorCol + if i >= len(row) { + row = append(row, widget.TextGridCell{Rune: r}) + } else { + row[i].Rune = r + } + } t.content.SetRow(t.cursorRow, row) + // TODO this needs to apply to styles too, how do we do this? if t.cursorRow == int(t.config.Rows-1) { for i = 0; i < t.cursorRow; i++ { t.content.SetRow(i, t.content.Row(i+1)) } - t.content.SetRow(i, []rune{}) + t.content.SetRow(i, []widget.TextGridCell{}) } else { t.cursorRow++ } @@ -88,6 +100,10 @@ func (t *Terminal) handleOutput(buf []byte) { case '\t': // TODO remove silly approximation out += " " default: + if currentFG != nil { + // TODO not if we discard out + t.setCellStyle(t.cursorRow, t.cursorCol+len(out), currentFG) + } out += string(r) } esc = -5 @@ -99,12 +115,39 @@ func (t *Terminal) handleOutput(buf []byte) { return } row := t.content.Row(t.cursorRow) - row = append(row, []rune(out)...) + for i, r := range out { + i += t.cursorCol + if i >= len(row) { + row = append(row, widget.TextGridCell{Rune: r}) + } else { + row[i].Rune = r + } + } t.content.SetRow(t.cursorRow, row) t.cursorCol += len(out) t.Refresh() } +func (t *Terminal) setCellStyle(row, col int, fgStyle color.Color) { + if row < 0 { + return + } + for len(t.content.Content) <= row { + t.content.Content = append(t.content.Content, []widget.TextGridCell{}) + } + + line := t.content.Row(row) + if col < 0 { + return + } + + for len(line) <= col { + line = append(line, widget.TextGridCell{}) + } + t.content.SetRow(row, line) + line[col].TextColor = fgStyle +} + func (t *Terminal) ringBell() { t.bell = true t.Refresh() diff --git a/screenshot.png b/screenshot.png index aaec5d15..b5ef0145 100644 Binary files a/screenshot.png and b/screenshot.png differ diff --git a/vendor/fyne.io/fyne/CHANGELOG.md b/vendor/fyne.io/fyne/CHANGELOG.md index ce815275..640eb7cb 100644 --- a/vendor/fyne.io/fyne/CHANGELOG.md +++ b/vendor/fyne.io/fyne/CHANGELOG.md @@ -19,11 +19,31 @@ More detailed release notes can be found on the [releases page](https://github.c * -## 1.2.3 - Ongoing +## 1.2.3 - 2 March 2020 + +### Added + + * Add media and volume icons to default themes (#649) + * Add Canvas.PixelCoordinateForPosition to find pixel locations if required + * Add ProgressInfinite dialog + +### Changed + + * Warn if -executable or -sourceDir flags are used for package on mobile (#652) + * Update scale based on device for mobile apps + * Windows without a title will now be named "Fyne Application" + * Revert fix to quit mobile apps - this is not allowed in guidelines ### Fixed * App.UniqueID() did not return current app ID + * Fyne package ignored -name flag for ios and android builds (#657) + * Possible crash when appending tabs to TabContainer + * FixedSize windows not rescaling when dragged between monitors (#654) + * Fix issues where older Android devices may not background or rotate (#677) + * Crash when setting theme before window content set (#688) + * Correct form extend behaviour (#694) + * Select drop-down width is wrong if the drop-down is too tall for the window (#706) ## 1.2.2 - 29 January 2020 diff --git a/vendor/fyne.io/fyne/CONTRIBUTING.md b/vendor/fyne.io/fyne/CONTRIBUTING.md new file mode 100644 index 00000000..ba3f1803 --- /dev/null +++ b/vendor/fyne.io/fyne/CONTRIBUTING.md @@ -0,0 +1,61 @@ +Thanks very much for your interest in contributing to Fyne! +The community is what makes this project successful and we are glad to welcome you on board. + +There are various ways to contribute, perhaps the following helps you know how to get started. + +## Reporting a bug + +If you've found something wrong we want to know about it, please help us understand the problem so we can resolve it. + +1. Check to see if this already is recorded, if so add some more information [issue list](/fyne-io/fyne/issues) +2. If not then create a new issue using the [bug report template](https://github.com/fyne-io/fyne/issues/new?assignees=&labels=&template=bug_report.md&title=) +3. Stay involved in the conversation on the issue as it is triaged and progressed. + + +## Fixing an issue + +Great! You found an issue and figured you can fix it for us. +If you can follow these steps then your code should get accepted fast. + +1. Read through the "Contributing Code" section further down this page. +2. Write a unit test to show it is broken. +3. Create the fix and you should see the test passes. +4. Run the tests and make sure everything still works as expected using `go test ./...`. +5. [Open a PR](https://github.com/fyne-io/fyne/compare) and work through the review checklist. + + +## Adding a feature + +It's always good news to hear that people want to contribute functionality. +But first of all check that it fits within our [[Vision]] and if we are already considering it on our [[Roadmap]]. +If you're not sure then you should join our #fyne-contributors channel on the gophers Slack server + +Once you are ready to code then the following steps should give you a smooth process: + +1. Read through the "Contributing Code" section further down this page. +2. Think about how you would structure your code and how it can be tested. +3. Write some code and enjoy the ease of writing Go code for even a complex project :). +4. Run the tests and make sure everything still works as expected using `go test ./...`. +5. [Open a PR](https://github.com/fyne-io/fyne/compare) and work through the review checklist. + + +# Contributing Code + +We aim to maintain a very high standard of code, through design, test and implementation. +To manage this we have various checks and processes in place that everyone should follow, including: + +* We use the Go standard format (with tabs not spaces) - you can run `gofmt` before committing +* Imports should be ordered according to the GoImports spec - you can use the `goimports` tool instead of `gofmt`. +* Everything should have a unit test attached (as much as possible, to keep our coverage up) + +# Decision Process + +The following points apply to our decision making process: + +* Any decisions or votes will be opened on the #fyne-contributors channel and follows lazy consensus. +* Any contributors not responding in 4 days will be deemed in agreement. +* Any PR that has not been responded to within 7 days can be automatically approved. +* No functionality will be added unless at least 2 developers agree it belongs. + +Bear in mind that this is a cross platform project so any new features would normally +be required to work on multiple desktop and mobile platforms. diff --git a/vendor/fyne.io/fyne/README-mobile.md b/vendor/fyne.io/fyne/README-mobile.md index d191834b..d0758576 100644 --- a/vendor/fyne.io/fyne/README-mobile.md +++ b/vendor/fyne.io/fyne/README-mobile.md @@ -40,6 +40,26 @@ The compiled applications above can be tested locally and even shared with developers that are part of your team. To deliver the applications to an app store or marketplace takes more work which has not yet been automated. +## Android + +To release your app needs to have debugging turned off. If you are using a custom manifest file then +you should add the following to AndroidManifest.xml: + +`` + +This file should be placed in the `main` package, where you are running your builds from. +If you have not set up a manifest file then fyne can make the changes for you by +adding a `-release` parameter to your package build: + +`$ fyne package -os "android" -release ...` + +You may also need to correct the alignment of the APK file before uploading it to the +Play Store, the following command should work: + +`${ANDROID_HOME}/build-tools/zipalign 4 myapp.apk aligned.apk` + +The aligned file can then be uploaded for distribution. + ## iOS The above steps will create a .app folder that can be installed to your diff --git a/vendor/fyne.io/fyne/README.md b/vendor/fyne.io/fyne/README.md index 3ede3eb8..ee4b6d4d 100644 --- a/vendor/fyne.io/fyne/README.md +++ b/vendor/fyne.io/fyne/README.md @@ -1,8 +1,7 @@

- GoDoc Reference - 1.2.2 release + Go API Reference + 1.2.3 release Join us on Slack - Support Fyne.io
Code Status Build Status @@ -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