├── resource ├── sample │ └── sample.gif ├── document │ ├── screenshot.gif │ ├── screenshot.png │ ├── media-play.svg │ ├── media-pause.svg │ ├── media-skip-previous.svg │ ├── media-skip-next.svg │ ├── media-fast-forward.svg │ ├── media-fast-rewind.svg │ ├── zoom-out.svg │ └── zoom-in.svg └── icons │ ├── README.md │ └── svg │ ├── image.svg │ ├── close.svg │ ├── open.svg │ └── credits.svg ├── go.mod ├── image ├── gif_test.go └── gif.go ├── Makefile ├── about.go ├── LICENSE ├── thumbnail.go ├── player.go ├── info.go ├── README.md ├── view.go ├── main.go ├── go.sum ├── bar.go ├── resource.go └── credits.go /resource/sample/sample.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lusingander/go-gif-viewer/HEAD/resource/sample/sample.gif -------------------------------------------------------------------------------- /resource/document/screenshot.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lusingander/go-gif-viewer/HEAD/resource/document/screenshot.gif -------------------------------------------------------------------------------- /resource/document/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lusingander/go-gif-viewer/HEAD/resource/document/screenshot.png -------------------------------------------------------------------------------- /resource/icons/README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | $ go get fyne.io/fyne/cmd/fyne 3 | $ fyne bundle ./svg > ../../resource.go 4 | ``` 5 | 6 | by http://www.evericons.com/ 7 | -------------------------------------------------------------------------------- /resource/document/media-play.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resource/document/media-pause.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resource/document/media-skip-previous.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resource/document/media-skip-next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resource/document/media-fast-forward.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resource/document/media-fast-rewind.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/lusingander/go-gif-viewer 2 | 3 | go 1.13 4 | 5 | require ( 6 | fyne.io/fyne v1.4.3 7 | github.com/dustin/go-humanize v1.0.0 8 | github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 9 | ) 10 | -------------------------------------------------------------------------------- /image/gif_test.go: -------------------------------------------------------------------------------- 1 | package image 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestLoadGif(t *testing.T) { 8 | _, err := LoadGIFImageFromPath("../resource/sample/sample.gif") 9 | if err != nil { 10 | t.Fatalf("failed to load: %v", err) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all run build bundle clean credits 2 | 3 | BINARY_NAME=go-gif-viewer 4 | 5 | all: build 6 | 7 | run: 8 | go run *.go 9 | 10 | build: bundle 11 | go build -o $(BINARY_NAME) 12 | 13 | bundle: 14 | fyne bundle ./resource/icons/svg/ > ./resource.go 15 | 16 | clean: 17 | rm $(BINARY_NAME) 18 | 19 | credits: 20 | fyne-credits-generator > ./credits.go -------------------------------------------------------------------------------- /resource/document/zoom-out.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resource/document/zoom-in.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /about.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/url" 6 | 7 | "fyne.io/fyne" 8 | "fyne.io/fyne/widget" 9 | ) 10 | 11 | const ( 12 | aboutWindowName = "About" 13 | repositoryURL = "https://github.com/lusingander/go-gif-viewer" 14 | ) 15 | 16 | func newAboutWindow() fyne.Window { 17 | w := fyne.CurrentApp().NewWindow(aboutWindowName) 18 | w.SetContent( 19 | widget.NewVBox( 20 | widget.NewHBox( 21 | widget.NewLabelWithStyle("go-gif-viewer", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}), 22 | widget.NewLabel(fmt.Sprintf("(Version %s)", version)), 23 | ), 24 | widget.NewHyperlink(repositoryURL, parseRepositoryURL()), 25 | ), 26 | ) 27 | return w 28 | } 29 | 30 | func parseRepositoryURL() *url.URL { 31 | u, _ := url.Parse(repositoryURL) 32 | return u 33 | } 34 | -------------------------------------------------------------------------------- /resource/icons/svg/image.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resource/icons/svg/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resource/icons/svg/open.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resource/icons/svg/credits.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 lusingander 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /thumbnail.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "fyne.io/fyne" 7 | "fyne.io/fyne/canvas" 8 | "fyne.io/fyne/layout" 9 | "fyne.io/fyne/widget" 10 | "github.com/lusingander/go-gif-viewer/image" 11 | "github.com/nfnt/resize" 12 | ) 13 | 14 | const ( 15 | thumbnailListWindowName = "List" 16 | ) 17 | 18 | var ( 19 | thumbnailListDefaultWidth = 150 20 | thumbnailListDefaultHeight = 400 21 | thumbnailListDefaultSize = fyne.NewSize(thumbnailListDefaultWidth, thumbnailListDefaultHeight) 22 | ) 23 | 24 | func thumbnailContainer(img *canvas.Image, i int) fyne.CanvasObject { 25 | label := fmt.Sprintf("%d", i+1) 26 | return widget.NewGroup(label, img) 27 | } 28 | 29 | func addThumbnails(container *fyne.Container, gif *image.GIFImage) { 30 | size := uint(thumbnailListDefaultWidth) 31 | for i := 0; i < gif.Length(); i++ { 32 | resized := resize.Resize(size, size, gif.Get(i), resize.NearestNeighbor) 33 | img := &canvas.Image{ 34 | Image: resized, 35 | FillMode: canvas.ImageFillOriginal, 36 | } 37 | container.AddObject(thumbnailContainer(img, i)) 38 | } 39 | } 40 | 41 | func newThumbnailListWindow(gif *image.GIFImage) fyne.Window { 42 | w := fyne.CurrentApp().NewWindow(thumbnailListWindowName) 43 | l := fyne.NewContainerWithLayout(layout.NewVBoxLayout()) 44 | w.SetContent(widget.NewVScrollContainer(l)) 45 | w.Resize(thumbnailListDefaultSize) 46 | go addThumbnails(l, gif) 47 | return w 48 | } 49 | -------------------------------------------------------------------------------- /player.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/lusingander/go-gif-viewer/image" 7 | ) 8 | 9 | type player struct { 10 | delayMilliSec int // TODO: fix 11 | speed float64 12 | 13 | observers []func() 14 | stopPlay chan bool 15 | changeSpeed chan float64 16 | } 17 | 18 | func newPlayer(i *image.GIFImage, speed float64) *player { 19 | return &player{ 20 | delayMilliSec: i.DelayMilliSec()[0], // TODO: fix 21 | speed: speed, 22 | observers: make([]func(), 0), 23 | changeSpeed: make(chan float64), 24 | } 25 | } 26 | 27 | func (p *player) addObserver(f func()) { 28 | p.observers = append(p.observers, f) 29 | } 30 | 31 | func (p *player) calcWaitDuration() time.Duration { 32 | ms := float64(p.delayMilliSec) * float64(time.Millisecond) / p.speed 33 | return time.Duration(ms) 34 | } 35 | 36 | func (p *player) play() { 37 | if p.stopPlay != nil { 38 | return 39 | } 40 | p.stopPlay = make(chan bool) 41 | go func() { 42 | t := time.NewTicker(p.calcWaitDuration()) 43 | for { 44 | select { 45 | case <-t.C: 46 | for _, o := range p.observers { 47 | o() 48 | } 49 | case <-p.changeSpeed: 50 | t = time.NewTicker(p.calcWaitDuration()) 51 | case <-p.stopPlay: 52 | t.Stop() 53 | return 54 | } 55 | } 56 | }() 57 | } 58 | 59 | func (p *player) pause() { 60 | if p.stopPlay == nil { 61 | return 62 | } 63 | p.stopPlay <- true 64 | p.stopPlay = nil 65 | } 66 | 67 | func (p *player) playing() bool { 68 | return p.stopPlay != nil 69 | } 70 | 71 | func (p *player) setSpeed(s float64) { 72 | p.speed = s 73 | if p.playing() { 74 | p.changeSpeed <- s 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /info.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "fyne.io/fyne" 7 | "fyne.io/fyne/layout" 8 | "fyne.io/fyne/widget" 9 | "github.com/dustin/go-humanize" 10 | "github.com/lusingander/go-gif-viewer/image" 11 | ) 12 | 13 | const ( 14 | infoWindowName = "Info" 15 | ) 16 | 17 | func newInfoWindow(gif *image.GIFImage) fyne.Window { 18 | w := fyne.CurrentApp().NewWindow(infoWindowName) 19 | w.SetContent( 20 | fyne.NewContainerWithLayout( 21 | layout.NewHBoxLayout(), 22 | fyne.NewContainerWithLayout( 23 | layout.NewVBoxLayout(), 24 | keyLabel("File name"), 25 | keyLabel("File path"), 26 | keyLabel("Last updated"), 27 | keyLabel("File size"), 28 | keyLabel("Image size"), 29 | keyLabel("Frame count"), 30 | ), 31 | fyne.NewContainerWithLayout( 32 | layout.NewVBoxLayout(), 33 | valueLabel(gif.FileName()), 34 | valueLabel(gif.FilePath()), 35 | valueLabel(formatLastUpdated(gif)), 36 | valueLabel(formatFileSize(gif)), 37 | valueLabel(formatImageSize(gif)), 38 | valueLabel(gif.Length()), 39 | ), 40 | ), 41 | ) 42 | return w 43 | } 44 | 45 | func formatFileSize(gif *image.GIFImage) string { 46 | sizeByte := gif.FileSizeByte() 47 | return fmt.Sprintf("%s (%d bytes)", humanize.Bytes(uint64(sizeByte)), sizeByte) 48 | } 49 | 50 | func formatImageSize(gif *image.GIFImage) string { 51 | w, h := gif.Size() 52 | return fmt.Sprintf("%d x %d", w, h) 53 | } 54 | 55 | func formatLastUpdated(gif *image.GIFImage) string { 56 | t := gif.FileLastUpdated() 57 | return t.Format("2006-01-02 03:04:56") 58 | } 59 | 60 | func keyLabel(l string) *widget.Label { 61 | return widget.NewLabelWithStyle(l+":", fyne.TextAlignTrailing, fyne.TextStyle{}) 62 | } 63 | 64 | func valueLabel(v interface{}) *widget.Label { 65 | return widget.NewLabelWithStyle(fmt.Sprintf("%v", v), fyne.TextAlignLeading, fyne.TextStyle{}) 66 | } 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/lusingander/go-gif-viewer) 2 | [![Go Report Card](https://goreportcard.com/badge/github.com/lusingander/go-gif-viewer)](https://goreportcard.com/report/github.com/lusingander/go-gif-viewer) 3 | ![GitHub](https://img.shields.io/github/license/lusingander/go-gif-viewer) 4 | 5 | # go-gif-viewer 6 | 7 | Simple animated GIF viewer with Go and [Fyne](https://fyne.io/) 8 | 9 | 10 | 11 | ## Installation 12 | 13 | `$ go get github.com/lusingander/go-gif-viewer` 14 | 15 | ## Usage 16 | 17 | `$ go-gif-viewer sample.gif` 18 | 19 | or 20 | 21 | `$ go-gif-viewer` and select open file icon (). 22 | 23 | ### Keybindings 24 | 25 | |Key|Description|Icon| 26 | |-|-|-| 27 | |←|Go to previous frame|| 28 | |→|Go to next frame|| 29 | |↑|Go to first frame|| 30 | |↓|Go to last frame|| 31 | |Space|Play / Pause| / | 32 | |||| 33 | |+|Zoom in|| 34 | |-|Zoom out|| 35 | |[|Decrease playback speed|-| 36 | |]|Increase playback speed|-| 37 | |||| 38 | |⌘O|Open image file|| 39 | |⌘W|Close image file|| 40 | 41 | ## Screenshot 42 | 43 | 44 | 45 | ---- 46 | 47 | Sample image: By Marvel - Based upon a NASA image, see [1]., CC BY-SA 3.0, Link 48 | -------------------------------------------------------------------------------- /view.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | im "image" 5 | 6 | "fyne.io/fyne" 7 | "fyne.io/fyne/canvas" 8 | "fyne.io/fyne/layout" 9 | "fyne.io/fyne/widget" 10 | "github.com/lusingander/go-gif-viewer/image" 11 | "github.com/nfnt/resize" 12 | ) 13 | 14 | const ( 15 | scaleDefault = 1.0 16 | scaleMax = 2.0 - 0.01 17 | scaleMin = 0.1 + 0.01 18 | scaleStep = 0.1 19 | ) 20 | 21 | type imageView struct { 22 | *canvas.Image 23 | fyne.CanvasObject 24 | 25 | *image.GIFImage 26 | 27 | scale float64 28 | 29 | caches []im.Image 30 | } 31 | 32 | func newImageView() *imageView { 33 | image := &canvas.Image{ 34 | FillMode: canvas.ImageFillOriginal, 35 | } 36 | imageBox := widget.NewVBox( 37 | layout.NewSpacer(), 38 | widget.NewHBox( 39 | layout.NewSpacer(), 40 | image, 41 | layout.NewSpacer(), 42 | ), 43 | layout.NewSpacer(), 44 | ) 45 | canvas := widget.NewScrollContainer(imageBox) 46 | return &imageView{ 47 | Image: image, 48 | CanvasObject: canvas, 49 | scale: scaleDefault, 50 | } 51 | } 52 | 53 | func (v *imageView) zoomIn() { 54 | if v.scale < scaleMax { 55 | v.scale += scaleStep 56 | v.loadImageCaches() 57 | } 58 | } 59 | 60 | func (v *imageView) zoomOut() { 61 | if v.scale > scaleMin { 62 | v.scale -= scaleStep 63 | v.loadImageCaches() 64 | } 65 | } 66 | 67 | func (v *imageView) setImage(img *image.GIFImage) { 68 | v.GIFImage = img 69 | v.scale = scaleDefault 70 | v.loadImageCaches() 71 | v.refleshFrame(0) 72 | } 73 | 74 | func (v *imageView) loadImageCaches() { 75 | l := v.GIFImage.Length() 76 | caches := make([]im.Image, l) 77 | for i := 0; i < l; i++ { 78 | img := v.GIFImage.Get(i) 79 | w, h := v.scaledImageSize() 80 | caches[i] = resize.Resize(w, h, img, resize.NearestNeighbor) 81 | } 82 | v.caches = caches 83 | } 84 | 85 | func (v *imageView) clearImage() { 86 | v.Image.Image = nil 87 | v.reflesh() 88 | } 89 | 90 | func (v *imageView) refleshFrame(n int) { 91 | v.Image.Image = v.caches[n] 92 | v.reflesh() 93 | } 94 | 95 | func (v *imageView) reflesh() { 96 | canvas.Refresh(v.Image) 97 | } 98 | 99 | func (v *imageView) scaledImageSize() (uint, uint) { 100 | w, h := v.GIFImage.Size() 101 | fw := float64(w) * v.scale 102 | fh := float64(h) * v.scale 103 | return uint(fw), uint(fh) 104 | } 105 | -------------------------------------------------------------------------------- /image/gif.go: -------------------------------------------------------------------------------- 1 | package image 2 | 3 | import ( 4 | "image" 5 | "image/color" 6 | "image/gif" 7 | "os" 8 | "path/filepath" 9 | "time" 10 | ) 11 | 12 | type fileInfo struct { 13 | name string 14 | path string 15 | sizeByte int64 16 | lastUpdated time.Time 17 | } 18 | 19 | // GIFImage represents GIF iamge file that can be displayed in frame units. 20 | type GIFImage struct { 21 | origin *gif.GIF 22 | *fileInfo 23 | images []image.Image 24 | } 25 | 26 | // FileName returns the original file name. 27 | func (g *GIFImage) FileName() string { 28 | return g.fileInfo.name 29 | } 30 | 31 | // FilePath returns the original file path. 32 | func (g *GIFImage) FilePath() string { 33 | return g.fileInfo.path 34 | } 35 | 36 | // FileSizeByte returns the original file size in bytes. 37 | func (g *GIFImage) FileSizeByte() int64 { 38 | return g.fileInfo.sizeByte 39 | } 40 | 41 | // FileLastUpdated returns the original file last updated datetime. 42 | func (g *GIFImage) FileLastUpdated() time.Time { 43 | return g.fileInfo.lastUpdated 44 | } 45 | 46 | // Size returns width and height of the image. 47 | func (g *GIFImage) Size() (w, h int) { 48 | c := g.origin.Config 49 | return c.Width, c.Height 50 | } 51 | 52 | // Get returns the image of ith frame. 53 | func (g *GIFImage) Get(i int) image.Image { 54 | return g.images[i] 55 | } 56 | 57 | // Length returns the number of frames. 58 | func (g *GIFImage) Length() int { 59 | return len(g.images) 60 | } 61 | 62 | // DelayMilliSec returns delay times in milliseconds. 63 | func (g *GIFImage) DelayMilliSec() []int { 64 | delay := make([]int, g.Length()) 65 | for i, d := range g.origin.Delay { 66 | delay[i] = d * 10 67 | } 68 | return delay 69 | } 70 | 71 | // LoadGIFImageFromPath reads the specified gif image file. 72 | func LoadGIFImageFromPath(path string) (*GIFImage, error) { 73 | g, i, err := loadGIF(path) 74 | if err != nil { 75 | return nil, err 76 | } 77 | return newGIFImage(g, i) 78 | } 79 | 80 | func newGIFImage(g *gif.GIF, i *fileInfo) (*GIFImage, error) { 81 | rect := g.Image[0].Rect 82 | images := make([]image.Image, len(g.Image)) 83 | images[0] = g.Image[0] 84 | for i := 1; i < len(g.Image); i++ { 85 | images[i] = restoreFrame(g.Image[i], images[i-1], rect) 86 | } 87 | return &GIFImage{ 88 | origin: g, 89 | fileInfo: i, 90 | images: images, 91 | }, nil 92 | } 93 | 94 | func restoreFrame(current *image.Paletted, prev image.Image, rect image.Rectangle) image.Image { 95 | img := image.NewRGBA(rect) 96 | for x := 0; x < rect.Dx(); x++ { 97 | for y := 0; y < rect.Dy(); y++ { 98 | if isInRect(x, y, current.Rect) && isOpaque(current.At(x, y)) { 99 | img.Set(x, y, current.At(x, y)) 100 | } else { 101 | img.Set(x, y, prev.At(x, y)) 102 | } 103 | } 104 | } 105 | return img 106 | } 107 | 108 | func loadGIF(path string) (*gif.GIF, *fileInfo, error) { 109 | f, err := os.Open(path) 110 | if err != nil { 111 | return nil, nil, err 112 | } 113 | defer f.Close() 114 | i, err := getFileInfo(f, path) 115 | if err != nil { 116 | return nil, nil, err 117 | } 118 | g, err := gif.DecodeAll(f) 119 | if err != nil { 120 | return nil, nil, err 121 | } 122 | return g, i, nil 123 | } 124 | 125 | func getFileInfo(f *os.File, p string) (*fileInfo, error) { 126 | i, err := f.Stat() 127 | if err != nil { 128 | return nil, err 129 | } 130 | path, err := filepath.Abs(p) 131 | if err != nil { 132 | return nil, err 133 | } 134 | return &fileInfo{ 135 | name: i.Name(), 136 | path: path, 137 | sizeByte: i.Size(), 138 | lastUpdated: i.ModTime(), 139 | }, nil 140 | } 141 | 142 | func isOpaque(c color.Color) bool { 143 | _, _, _, a := c.RGBA() 144 | return a > 0 145 | } 146 | 147 | func isInRect(x, y int, r image.Rectangle) bool { 148 | return r.Min.X <= x && x < r.Max.X && 149 | r.Min.Y <= y && y < r.Max.Y 150 | } 151 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | 7 | "fyne.io/fyne" 8 | "fyne.io/fyne/app" 9 | "fyne.io/fyne/dialog" 10 | "fyne.io/fyne/driver/desktop" 11 | "fyne.io/fyne/layout" 12 | "fyne.io/fyne/storage" 13 | "github.com/lusingander/go-gif-viewer/image" 14 | ) 15 | 16 | const ( 17 | appName = "GIF Viewer" 18 | version = "0.3.0" 19 | ) 20 | 21 | var defaultWindowSize = fyne.NewSize(400, 400) 22 | 23 | type mainView struct { 24 | fyne.Window 25 | 26 | *fyne.Container 27 | 28 | *menuBar 29 | *imageView 30 | *navigateBar 31 | 32 | *player 33 | 34 | isThumbnailListWindowOpening bool 35 | isInfoWindowOpening bool 36 | isCreditsWindowOpening bool 37 | isAboutWindowOpening bool 38 | } 39 | 40 | func newMainView(w fyne.Window) *mainView { 41 | mainView := &mainView{ 42 | Window: w, 43 | } 44 | menuBar := newMenuBar( 45 | mainView.openFileDialog, 46 | mainView.clearImage, 47 | mainView.thumbnailList, 48 | mainView.info, 49 | mainView.credits, 50 | mainView.about, 51 | mainView.zoomIn, 52 | mainView.zoomOut, 53 | ) 54 | imageView := newImageView() 55 | navigateBar := newNavigateBar() 56 | panel := fyne.NewContainerWithLayout( 57 | layout.NewBorderLayout(menuBar.CanvasObject, navigateBar.CanvasObject, nil, nil), 58 | menuBar.CanvasObject, navigateBar.CanvasObject, imageView.CanvasObject, 59 | ) 60 | mainView.Container = panel 61 | mainView.menuBar = menuBar 62 | mainView.imageView = imageView 63 | mainView.navigateBar = navigateBar 64 | return mainView 65 | } 66 | 67 | func (v *mainView) loadImageFromPath(path string) error { 68 | img, err := image.LoadGIFImageFromPath(path) 69 | if err != nil { 70 | return err 71 | } 72 | v.loadImage(img) 73 | return nil 74 | } 75 | 76 | func (v *mainView) loadImage(img *image.GIFImage) { 77 | v.imageView.setImage(img) 78 | v.navigateBar.setImage(img) 79 | v.navigateBar.addObserver(v.refleshFrame) 80 | 81 | v.player = newPlayer(img, v.menuBar.currentSpeed()) 82 | v.player.addObserver(v.navigateBar.next) 83 | v.menuBar.setPlayer(v.player) 84 | v.navigateBar.setPlayer(v.player) 85 | } 86 | 87 | func (v *mainView) clearImage() { 88 | v.imageView.clearImage() 89 | v.navigateBar.clearImage() 90 | 91 | v.player = nil 92 | v.menuBar.setPlayer(v.player) 93 | v.navigateBar.setPlayer(v.player) 94 | } 95 | 96 | func (v *mainView) openFileDialog() { 97 | fd := dialog.NewFileOpen(func(reader fyne.URIReadCloser, err error) { 98 | if err == nil && reader == nil { 99 | return 100 | } 101 | if err != nil { 102 | dialog.ShowError(err, v.Window) 103 | return 104 | } 105 | v.withLoadingDialog(func() { 106 | err = v.loadImageFromPath(reader.URI().String()[7:]) // `file://` 107 | if err != nil { 108 | dialog.ShowError(err, v.Window) 109 | return 110 | } 111 | }) 112 | }, v.Window) 113 | fd.SetFilter(storage.NewExtensionFileFilter([]string{".gif"})) 114 | fd.Show() 115 | } 116 | 117 | func (v *mainView) withLoadingDialog(f func()) { 118 | d := dialog.NewProgressInfinite("Loading", "Now loading...", v.Window) 119 | go func() { 120 | f() 121 | d.Hide() 122 | }() 123 | d.Show() 124 | } 125 | 126 | func (v *mainView) thumbnailList() { 127 | if v.imageView.GIFImage == nil { 128 | return 129 | } 130 | wf := func() fyne.Window { return newThumbnailListWindow(v.imageView.GIFImage) } 131 | v.openWindow(wf, &v.isThumbnailListWindowOpening) 132 | } 133 | 134 | func (v *mainView) info() { 135 | if v.imageView.GIFImage == nil { 136 | return 137 | } 138 | wf := func() fyne.Window { return newInfoWindow(v.imageView.GIFImage) } 139 | v.openWindow(wf, &v.isInfoWindowOpening) 140 | } 141 | 142 | func (v *mainView) credits() { 143 | wf := func() fyne.Window { return CreditsWindow(fyne.CurrentApp(), fyne.NewSize(800, 400)) } 144 | v.openWindow(wf, &v.isCreditsWindowOpening) 145 | } 146 | 147 | func (v *mainView) about() { 148 | v.openWindow(newAboutWindow, &v.isAboutWindowOpening) 149 | } 150 | 151 | func (v *mainView) openWindow(windowOpen func() fyne.Window, isOpening *bool) { 152 | if *isOpening { 153 | return 154 | } 155 | w := windowOpen() 156 | w.SetOnClosed(func() { *isOpening = false }) 157 | w.Show() 158 | *isOpening = true 159 | } 160 | 161 | func (v *mainView) zoomIn() { 162 | if v.imageView.GIFImage == nil { 163 | return 164 | } 165 | v.imageView.zoomIn() 166 | v.navigateBar.update() 167 | } 168 | 169 | func (v *mainView) zoomOut() { 170 | if v.imageView.GIFImage == nil { 171 | return 172 | } 173 | v.imageView.zoomOut() 174 | v.navigateBar.update() 175 | } 176 | 177 | func (v *mainView) handleKeys(e *fyne.KeyEvent) { 178 | switch e.Name { 179 | case fyne.KeyLeft: 180 | v.prev() 181 | case fyne.KeyRight: 182 | v.next() 183 | case fyne.KeyUp: 184 | v.first() 185 | case fyne.KeyDown: 186 | v.last() 187 | case fyne.KeySpace: 188 | v.pressPlayButton() 189 | } 190 | } 191 | 192 | func (v *mainView) handleRune(r rune) { 193 | switch r { 194 | case '+': 195 | v.zoomIn() 196 | case '-': 197 | v.zoomOut() 198 | case '[': 199 | v.decreaseSpeed() 200 | case ']': 201 | v.increaseSpeed() 202 | } 203 | } 204 | 205 | func (v *mainView) addShortcuts() { 206 | // TODO: want to use ctrl on Windows... 207 | v.addSuperShotrcuts(fyne.KeyO, v.openFileDialog) 208 | v.addSuperShotrcuts(fyne.KeyW, v.clearImage) 209 | } 210 | 211 | func (v *mainView) addSuperShotrcuts(key fyne.KeyName, f func()) { 212 | v.Window.Canvas().AddShortcut( 213 | &desktop.CustomShortcut{KeyName: key, Modifier: desktop.SuperModifier}, 214 | func(_ fyne.Shortcut) { f() }, 215 | ) 216 | } 217 | 218 | func run(args []string) error { 219 | a := app.New() 220 | w := a.NewWindow(appName) 221 | w.Resize(defaultWindowSize) 222 | v := newMainView(w) 223 | w.SetContent(v.Container) 224 | w.Canvas().SetOnTypedKey(v.handleKeys) 225 | w.Canvas().SetOnTypedRune(v.handleRune) 226 | v.addShortcuts() 227 | if len(args) > 1 { 228 | v.withLoadingDialog(func() { 229 | err := v.loadImageFromPath(args[1]) 230 | if err != nil { 231 | dialog.ShowError(err, w) 232 | } 233 | }) 234 | } 235 | w.ShowAndRun() 236 | return nil 237 | } 238 | 239 | func main() { 240 | if err := run(os.Args); err != nil { 241 | log.Fatal(err) 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | fyne.io/fyne v1.4.3 h1:356CnXCiYrrfaLGsB7qLK3c6ktzyh8WR05v/2RBu51I= 2 | fyne.io/fyne v1.4.3/go.mod h1:8kiPBNSDmuplxs9WnKCkaWYqbcXFy0DeAzwa6PBO9Z8= 3 | github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod h1:7uhhqiBaR4CpN0k9rMjOtjpcfGd6DG2m04zQxKnWQ0I= 4 | github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= 5 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 6 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 8 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 9 | github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= 10 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 11 | github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= 12 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 13 | github.com/fyne-io/mobile v0.1.2 h1:0HaXDtOOwyOTn3Umi0uKVCOgJtfX73c6unC4U8i5VZU= 14 | github.com/fyne-io/mobile v0.1.2/go.mod h1:/kOrWrZB6sasLbEy2JIvr4arEzQTXBTZGb3Y96yWbHY= 15 | github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 h1:SCYMcCJ89LjRGwEa0tRluNRiMjZHalQZrVrvTbPh+qw= 16 | github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk= 17 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200625191551-73d3c3675aa3 h1:q521PfSp5/z6/sD9FZZOWj4d1MLmfQW8PkRnI9M6PCE= 18 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200625191551-73d3c3675aa3/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 19 | github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME= 20 | github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 21 | github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff h1:W71vTCKoxtdXgnm1ECDFkfQnpdqAO00zzGXLA5yaEX8= 22 | github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff/go.mod h1:wfqRWLHRBsRgkp5dmbG56SA0DmVtwrF5N3oPdI8t+Aw= 23 | github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMHVoNcyXYq9otUupF7/h/2tmHlhrS2zw7ZVvUqc= 24 | github.com/josephspurrier/goversioninfo v0.0.0-20200309025242-14b0ab84c6ca/go.mod h1:eJTEwMjXb7kZ633hO3Ln9mBUCOjX2+FlTljvpl9SYdE= 25 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 26 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 27 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 28 | github.com/lucor/goinfo v0.0.0-20200401173949-526b5363a13a/go.mod h1:ORP3/rB5IsulLEBwQZCJyyV6niqmI7P4EWSmkug+1Ng= 29 | github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= 30 | github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= 31 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= 32 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 33 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 34 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 35 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 36 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 37 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 38 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 39 | github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564 h1:HunZiaEKNGVdhTRQOVpMmj5MQnGnv+e8uZNu3xFLgyM= 40 | github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564/go.mod h1:afMbS0qvv1m5tfENCwnOdZGOF8RGR/FsZ7bvBxQGZG4= 41 | github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9 h1:m59mIOBO4kfcNCEzJNy71UkeF4XIx2EVmL9KLwDQdmM= 42 | github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9/go.mod h1:mvWM0+15UqyrFKqdRjY6LuAVJR0HOVhJlEgZ5JWtSWU= 43 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 44 | github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= 45 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 46 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 47 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 48 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 49 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= 50 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 51 | golang.org/x/image v0.0.0-20200430140353-33d19683fad8 h1:6WW6V3x1P/jokJBpRQYUJnMHRP6isStQwCozxnU7XQw= 52 | golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 53 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 54 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 55 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 56 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 57 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k= 58 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 59 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 60 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 61 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 62 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 63 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 64 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 65 | golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666 h1:gVCS+QOncANNPlmlO1AhlU3oxs4V9z+gTtPwIk3p2N8= 66 | golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 67 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 68 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 69 | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= 70 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 71 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 72 | golang.org/x/tools v0.0.0-20190808195139-e713427fea3f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 73 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 74 | golang.org/x/tools v0.0.0-20200328031815-3db5fc6bac03/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 75 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 76 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 77 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 78 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 79 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= 80 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 81 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 82 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 83 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 84 | -------------------------------------------------------------------------------- /bar.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | 7 | "fyne.io/fyne" 8 | "fyne.io/fyne/layout" 9 | "fyne.io/fyne/theme" 10 | "fyne.io/fyne/widget" 11 | "github.com/lusingander/go-gif-viewer/image" 12 | ) 13 | 14 | var ( 15 | openIcon fyne.Resource = theme.NewThemedResource(resourceOpenSvg, nil) 16 | closeIcon fyne.Resource = theme.NewThemedResource(resourceCloseSvg, nil) 17 | 18 | thumbnailIcon fyne.Resource = theme.NewThemedResource(resourceImageSvg, nil) 19 | infoIcon fyne.Resource = theme.NewThemedResource(theme.InfoIcon(), nil) 20 | creditsIcon fyne.Resource = theme.NewThemedResource(resourceCreditsSvg, nil) // TODO: better icon... 21 | aboutIcon fyne.Resource = theme.NewThemedResource(theme.QuestionIcon(), nil) 22 | 23 | zoomInIcon fyne.Resource = theme.NewThemedResource(theme.ZoomInIcon(), nil) 24 | zoomOutIcon fyne.Resource = theme.NewThemedResource(theme.ZoomOutIcon(), nil) 25 | 26 | playIcon fyne.Resource = theme.NewThemedResource(theme.MediaPlayIcon(), nil) 27 | pauseIcon fyne.Resource = theme.NewThemedResource(theme.MediaPauseIcon(), nil) 28 | 29 | prevIcon fyne.Resource = theme.NewThemedResource(theme.MediaFastRewindIcon(), nil) 30 | nextIcon fyne.Resource = theme.NewThemedResource(theme.MediaFastForwardIcon(), nil) 31 | firstIcon fyne.Resource = theme.NewThemedResource(theme.MediaSkipPreviousIcon(), nil) 32 | lastIcon fyne.Resource = theme.NewThemedResource(theme.MediaSkipNextIcon(), nil) 33 | ) 34 | 35 | var speeds = []string{ 36 | "0.25x", 37 | "0.5x", 38 | "1.0x", 39 | "1.5x", 40 | "2.0x", 41 | "3.0x", 42 | "4.0x", 43 | } 44 | 45 | func defaultSpeed() string { 46 | return speeds[2] // 1.0x 47 | } 48 | 49 | type menuBar struct { 50 | fyne.CanvasObject 51 | 52 | *widget.Toolbar 53 | *widget.Select 54 | 55 | *player 56 | } 57 | 58 | func createSpeedSelect() *widget.Select { 59 | sel := widget.NewSelect(speeds, func(string) {}) 60 | sel.SetSelected(defaultSpeed()) 61 | return sel 62 | } 63 | 64 | func newMenuBar(open, close, thumbnail, info, credits, about, zoomIn, zoomOut func()) *menuBar { 65 | toolBar := widget.NewToolbar( 66 | widget.NewToolbarAction(openIcon, open), 67 | widget.NewToolbarAction(closeIcon, close), 68 | widget.NewToolbarAction(thumbnailIcon, thumbnail), 69 | widget.NewToolbarAction(infoIcon, info), 70 | widget.NewToolbarAction(creditsIcon, credits), 71 | widget.NewToolbarAction(aboutIcon, about), 72 | widget.NewToolbarSpacer(), 73 | widget.NewToolbarAction(zoomInIcon, zoomIn), 74 | widget.NewToolbarAction(zoomOutIcon, zoomOut), 75 | ) 76 | sppedSelect := createSpeedSelect() 77 | return &menuBar{ 78 | CanvasObject: fyne.NewContainerWithLayout( 79 | layout.NewBorderLayout(nil, nil, nil, sppedSelect), 80 | sppedSelect, toolBar, 81 | ), 82 | Toolbar: toolBar, 83 | Select: sppedSelect, 84 | } 85 | } 86 | 87 | func (b *menuBar) setPlayer(p *player) { 88 | b.player = p 89 | b.Select.OnChanged = func(s string) { 90 | // TODO: fix 91 | if b.player != nil { 92 | b.player.setSpeed(parseSpeed(s)) 93 | } 94 | } 95 | b.Select.SetSelected(defaultSpeed()) 96 | } 97 | 98 | func (b *menuBar) currentSpeed() float64 { 99 | return parseSpeed(b.Select.Selected) 100 | } 101 | 102 | func parseSpeed(s string) float64 { 103 | f, _ := strconv.ParseFloat(s[:len(s)-1], 64) 104 | return f 105 | } 106 | 107 | func (b *menuBar) increaseSpeed() { 108 | // TODO: fix 109 | if b.player == nil { 110 | return 111 | } 112 | for i := 0; i < len(speeds)-1; i++ { 113 | if speeds[i] == b.Selected { 114 | b.SetSelected(speeds[i+1]) 115 | b.player.setSpeed(b.currentSpeed()) 116 | return 117 | } 118 | } 119 | } 120 | 121 | func (b *menuBar) decreaseSpeed() { 122 | // TODO: fix 123 | if b.player == nil { 124 | return 125 | } 126 | for i := 1; i < len(speeds); i++ { 127 | if speeds[i] == b.Selected { 128 | b.SetSelected(speeds[i-1]) 129 | b.player.setSpeed(b.currentSpeed()) 130 | return 131 | } 132 | } 133 | } 134 | 135 | type playButton struct { 136 | *widget.Button 137 | 138 | playing bool 139 | play, pause func() 140 | } 141 | 142 | func newPlayButton(play, pause func()) *playButton { 143 | return &playButton{ 144 | Button: widget.NewButtonWithIcon("", playIcon, play), 145 | playing: false, 146 | play: play, 147 | pause: pause, 148 | } 149 | } 150 | 151 | func (b *playButton) click() { 152 | if b.playing { 153 | b.OnTapped = b.play 154 | b.SetIcon(playIcon) 155 | } else { 156 | b.OnTapped = b.pause 157 | b.SetIcon(pauseIcon) 158 | } 159 | b.playing = !b.playing 160 | } 161 | 162 | type navigateBar struct { 163 | fyne.CanvasObject 164 | 165 | *widget.Label 166 | *widget.Slider 167 | *playButton 168 | *player 169 | 170 | current, total int 171 | totalDigit int 172 | 173 | observers []func(int) 174 | 175 | canPlay bool 176 | } 177 | 178 | func (b *navigateBar) addObserver(f func(int)) { 179 | b.observers = append(b.observers, f) 180 | } 181 | 182 | func (b *navigateBar) clearObservers() { 183 | b.observers = make([]func(int), 0) 184 | } 185 | 186 | func (b *navigateBar) pressPlayButton() { 187 | if b.player == nil { 188 | return 189 | } 190 | if b.player.playing() { 191 | b.stop() 192 | } else { 193 | b.start() 194 | } 195 | } 196 | 197 | func (b *navigateBar) start() { 198 | if !b.canPlay || b.player.playing() { 199 | return 200 | } 201 | b.playButton.click() 202 | b.player.play() 203 | } 204 | 205 | func (b *navigateBar) stop() { 206 | if !b.player.playing() { 207 | return 208 | } 209 | b.player.pause() 210 | b.playButton.click() 211 | } 212 | 213 | func (b *navigateBar) next() { 214 | if !b.canPlay { 215 | return 216 | } 217 | if b.current == b.total { 218 | b.first() 219 | } else if b.current < b.total { 220 | b.current++ 221 | b.update() 222 | } 223 | } 224 | 225 | func (b *navigateBar) prev() { 226 | if !b.canPlay { 227 | return 228 | } 229 | if b.current == 1 { 230 | b.last() 231 | } else if b.current > 1 { 232 | b.current-- 233 | b.update() 234 | } 235 | } 236 | 237 | func (b *navigateBar) first() { 238 | if !b.canPlay { 239 | return 240 | } 241 | if b.current > 1 { 242 | b.current = 1 243 | b.update() 244 | } 245 | } 246 | 247 | func (b *navigateBar) last() { 248 | if !b.canPlay { 249 | return 250 | } 251 | if b.current < b.total { 252 | b.current = b.total 253 | b.update() 254 | } 255 | } 256 | 257 | func (b *navigateBar) change(n int) { 258 | if !b.canPlay { 259 | return 260 | } 261 | if 1 <= n && n <= b.total { 262 | b.current = n 263 | b.update() 264 | } 265 | } 266 | 267 | func (b *navigateBar) update() { 268 | b.SetText(b.createCountText()) 269 | // Note: Slider doesn't have proper method... 270 | b.Slider.Value = float64(b.current - 1) 271 | b.Slider.Refresh() 272 | for _, o := range b.observers { 273 | o(b.current - 1) 274 | } 275 | } 276 | 277 | func (b *navigateBar) createCountText() string { 278 | if !b.canPlay { 279 | return "" 280 | } 281 | return fmt.Sprintf("%*d/%*d", 282 | b.totalDigit, b.current, b.totalDigit, b.total) 283 | } 284 | 285 | func (b *navigateBar) setImage(img *image.GIFImage) { 286 | b.clearObservers() 287 | n := img.Length() 288 | b.current = 1 289 | b.total = n 290 | b.Slider.Max = float64(n - 1) 291 | b.totalDigit = len(strconv.Itoa(n)) 292 | b.canPlay = true 293 | b.update() 294 | } 295 | 296 | func (b *navigateBar) clearImage() { 297 | b.clearObservers() 298 | b.current = 1 299 | b.Slider.Max = 1 300 | b.canPlay = false 301 | b.update() 302 | } 303 | 304 | func (b *navigateBar) setPlayer(p *player) { 305 | b.player = p 306 | } 307 | 308 | func newNavigateBar() *navigateBar { 309 | bar := &navigateBar{ 310 | observers: make([]func(int), 0), 311 | canPlay: false, 312 | } 313 | slider := createSliderBar(bar) 314 | buttons := createButtons(bar) 315 | bar.CanvasObject = widget.NewVBox(slider, buttons) 316 | return bar 317 | } 318 | 319 | func createSliderBar(bar *navigateBar) fyne.CanvasObject { 320 | bar.Slider = widget.NewSlider(0, 1) 321 | bar.Slider.OnChanged = func(f float64) { bar.change(int(f) + 1) } 322 | bar.Label = widget.NewLabel(bar.createCountText()) 323 | bar.Label.TextStyle.Monospace = true 324 | return fyne.NewContainerWithLayout( 325 | layout.NewBorderLayout(nil, nil, nil, bar.Label), 326 | bar.Label, bar.Slider, 327 | ) 328 | } 329 | 330 | func createButtons(bar *navigateBar) fyne.CanvasObject { 331 | bar.playButton = newPlayButton(bar.start, bar.stop) 332 | first := widget.NewButtonWithIcon("", firstIcon, bar.first) 333 | prev := widget.NewButtonWithIcon("", prevIcon, bar.prev) 334 | next := widget.NewButtonWithIcon("", nextIcon, bar.next) 335 | last := widget.NewButtonWithIcon("", lastIcon, bar.last) 336 | return widget.NewHBox( 337 | layout.NewSpacer(), first, prev, bar.playButton.Button, next, last, layout.NewSpacer(), 338 | ) 339 | } 340 | -------------------------------------------------------------------------------- /resource.go: -------------------------------------------------------------------------------- 1 | // auto-generated 2 | 3 | package main 4 | 5 | import "fyne.io/fyne" 6 | 7 | var resourceCloseSvg = &fyne.StaticResource{ 8 | StaticName: "close.svg", 9 | StaticContent: []byte{ 10 | 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, 10, 32, 32, 60, 112, 97, 116, 104, 32, 102, 105, 108, 108, 45, 114, 117, 108, 101, 61, 34, 101, 118, 101, 110, 111, 100, 100, 34, 32, 100, 61, 34, 77, 50, 49, 44, 53, 32, 67, 50, 50, 46, 49, 48, 52, 53, 54, 57, 53, 44, 53, 32, 50, 51, 44, 53, 46, 56, 57, 53, 52, 51, 48, 53, 32, 50, 51, 44, 55, 32, 76, 50, 51, 44, 49, 57, 32, 67, 50, 51, 44, 50, 48, 46, 49, 48, 52, 53, 54, 57, 53, 32, 50, 50, 46, 49, 48, 52, 53, 54, 57, 53, 44, 50, 49, 32, 50, 49, 44, 50, 49, 32, 76, 51, 44, 50, 49, 32, 67, 49, 46, 56, 57, 53, 52, 51, 48, 53, 44, 50, 49, 32, 49, 44, 50, 48, 46, 49, 48, 52, 53, 54, 57, 53, 32, 49, 44, 49, 57, 32, 76, 49, 44, 53, 32, 67, 49, 44, 51, 46, 56, 57, 53, 52, 51, 48, 53, 32, 49, 46, 56, 57, 53, 52, 51, 48, 53, 44, 51, 32, 51, 44, 51, 32, 76, 57, 44, 51, 32, 67, 49, 48, 46, 49, 50, 48, 48, 48, 50, 51, 44, 51, 32, 49, 48, 46, 56, 51, 50, 57, 51, 57, 44, 51, 46, 52, 55, 53, 52, 53, 49, 49, 56, 32, 49, 49, 46, 53, 52, 56, 57, 55, 54, 52, 44, 52, 46, 51, 55, 56, 56, 53, 51, 48, 57, 32, 67, 49, 49, 46, 53, 57, 54, 55, 53, 52, 55, 44, 52, 46, 52, 51, 57, 49, 51, 51, 53, 50, 32, 49, 49, 46, 56, 49, 48, 48, 57, 57, 57, 44, 52, 46, 55, 49, 53, 56, 56, 50, 55, 53, 32, 49, 49, 46, 56, 54, 50, 52, 56, 51, 49, 44, 52, 46, 55, 56, 48, 56, 49, 57, 52, 53, 32, 67, 49, 50, 46, 48, 49, 57, 55, 50, 54, 44, 52, 46, 57, 55, 53, 55, 52, 52, 57, 53, 32, 49, 50, 46, 48, 53, 49, 55, 55, 57, 53, 44, 52, 46, 57, 57, 57, 55, 50, 57, 53, 54, 32, 49, 50, 46, 48, 48, 49, 55, 56, 54, 51, 44, 53, 32, 76, 50, 49, 44, 53, 32, 90, 32, 77, 50, 49, 44, 49, 57, 32, 76, 50, 49, 44, 55, 32, 76, 49, 49, 46, 57, 57, 52, 54, 52, 54, 44, 54, 46, 57, 57, 57, 57, 56, 53, 54, 55, 32, 67, 49, 49, 46, 50, 55, 54, 52, 57, 49, 53, 44, 54, 46, 57, 57, 54, 49, 52, 48, 53, 56, 32, 49, 48, 46, 56, 48, 56, 54, 57, 49, 54, 44, 54, 46, 54, 53, 57, 57, 48, 57, 50, 51, 32, 49, 48, 46, 51, 48, 53, 56, 51, 50, 50, 44, 54, 46, 48, 51, 54, 53, 52, 49, 52, 54, 32, 67, 49, 48, 46, 50, 51, 54, 52, 50, 56, 49, 44, 53, 46, 57, 53, 48, 53, 48, 52, 57, 55, 32, 49, 48, 46, 48, 49, 53, 56, 55, 51, 55, 44, 53, 46, 54, 54, 52, 52, 48, 51, 57, 56, 32, 57, 46, 57, 56, 49, 53, 57, 55, 55, 56, 44, 53, 46, 54, 50, 49, 49, 53, 57, 49, 54, 32, 67, 57, 46, 54, 48, 55, 48, 50, 49, 53, 56, 44, 53, 46, 49, 52, 56, 53, 54, 56, 49, 49, 32, 57, 46, 51, 56, 52, 50, 52, 52, 52, 50, 44, 53, 32, 57, 44, 53, 32, 76, 51, 44, 53, 32, 76, 51, 44, 49, 57, 32, 76, 50, 49, 44, 49, 57, 32, 90, 32, 77, 49, 50, 44, 49, 49, 46, 53, 56, 53, 55, 56, 54, 52, 32, 76, 49, 52, 46, 50, 57, 50, 56, 57, 51, 50, 44, 57, 46, 50, 57, 50, 56, 57, 51, 50, 50, 32, 76, 49, 53, 46, 55, 48, 55, 49, 48, 54, 56, 44, 49, 48, 46, 55, 48, 55, 49, 48, 54, 56, 32, 76, 49, 51, 46, 52, 49, 52, 50, 49, 51, 54, 44, 49, 51, 32, 76, 49, 53, 46, 55, 48, 55, 49, 48, 54, 56, 44, 49, 53, 46, 50, 57, 50, 56, 57, 51, 50, 32, 76, 49, 52, 46, 50, 57, 50, 56, 57, 51, 50, 44, 49, 54, 46, 55, 48, 55, 49, 48, 54, 56, 32, 76, 49, 50, 44, 49, 52, 46, 52, 49, 52, 50, 49, 51, 54, 32, 76, 57, 46, 55, 48, 55, 49, 48, 54, 55, 56, 44, 49, 54, 46, 55, 48, 55, 49, 48, 54, 56, 32, 76, 56, 46, 50, 57, 50, 56, 57, 51, 50, 50, 44, 49, 53, 46, 50, 57, 50, 56, 57, 51, 50, 32, 76, 49, 48, 46, 53, 56, 53, 55, 56, 54, 52, 44, 49, 51, 32, 76, 56, 46, 50, 57, 50, 56, 57, 51, 50, 50, 44, 49, 48, 46, 55, 48, 55, 49, 48, 54, 56, 32, 76, 57, 46, 55, 48, 55, 49, 48, 54, 55, 56, 44, 57, 46, 50, 57, 50, 56, 57, 51, 50, 50, 32, 76, 49, 50, 44, 49, 49, 46, 53, 56, 53, 55, 56, 54, 52, 32, 90, 34, 47, 62, 10, 60, 47, 115, 118, 103, 62, 10}} 11 | 12 | var resourceCreditsSvg = &fyne.StaticResource{ 13 | StaticName: "credits.svg", 14 | StaticContent: []byte{ 15 | 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, 10, 32, 32, 60, 112, 97, 116, 104, 32, 102, 105, 108, 108, 45, 114, 117, 108, 101, 61, 34, 101, 118, 101, 110, 111, 100, 100, 34, 32, 100, 61, 34, 77, 54, 46, 48, 51, 53, 51, 49, 55, 55, 56, 44, 49, 56, 46, 55, 51, 57, 55, 54, 52, 32, 67, 55, 46, 54, 50, 51, 50, 57, 57, 55, 57, 44, 50, 48, 46, 49, 52, 54, 49, 55, 54, 32, 57, 46, 55, 49, 49, 57, 51, 57, 50, 53, 44, 50, 49, 32, 49, 50, 44, 50, 49, 32, 67, 49, 52, 46, 50, 56, 56, 48, 54, 48, 56, 44, 50, 49, 32, 49, 54, 46, 51, 55, 54, 55, 48, 48, 50, 44, 50, 48, 46, 49, 52, 54, 49, 55, 54, 32, 49, 55, 46, 57, 54, 52, 54, 56, 50, 50, 44, 49, 56, 46, 55, 51, 57, 55, 54, 52, 32, 67, 49, 55, 46, 54, 55, 49, 57, 57, 57, 52, 44, 49, 55, 46, 54, 56, 55, 51, 52, 57, 32, 49, 53, 46, 53, 54, 57, 51, 56, 50, 51, 44, 49, 55, 32, 49, 50, 44, 49, 55, 32, 67, 56, 46, 52, 51, 48, 54, 49, 55, 55, 52, 44, 49, 55, 32, 54, 46, 51, 50, 56, 48, 48, 48, 54, 53, 44, 49, 55, 46, 54, 56, 55, 51, 52, 57, 32, 54, 46, 48, 51, 53, 51, 49, 55, 55, 56, 44, 49, 56, 46, 55, 51, 57, 55, 54, 52, 32, 90, 32, 77, 52, 46, 54, 48, 48, 53, 48, 51, 53, 56, 44, 49, 55, 46, 49, 50, 52, 54, 52, 55, 53, 32, 67, 53, 46, 55, 50, 53, 57, 53, 49, 51, 49, 44, 49, 53, 46, 54, 51, 56, 48, 54, 52, 32, 56, 46, 51, 55, 48, 54, 48, 49, 56, 57, 44, 49, 53, 32, 49, 50, 44, 49, 53, 32, 67, 49, 53, 46, 54, 50, 57, 51, 57, 56, 49, 44, 49, 53, 32, 49, 56, 46, 50, 55, 52, 48, 52, 56, 55, 44, 49, 53, 46, 54, 51, 56, 48, 54, 52, 32, 49, 57, 46, 51, 57, 57, 52, 57, 54, 52, 44, 49, 55, 46, 49, 50, 52, 54, 52, 55, 53, 32, 67, 50, 48, 46, 52, 48, 56, 54, 49, 55, 57, 44, 49, 53, 46, 54, 55, 48, 51, 49, 56, 51, 32, 50, 49, 44, 49, 51, 46, 57, 48, 52, 50, 50, 49, 53, 32, 50, 49, 44, 49, 50, 32, 67, 50, 49, 44, 55, 46, 48, 50, 57, 52, 51, 55, 50, 53, 32, 49, 54, 46, 57, 55, 48, 53, 54, 50, 55, 44, 51, 32, 49, 50, 44, 51, 32, 67, 55, 46, 48, 50, 57, 52, 51, 55, 50, 53, 44, 51, 32, 51, 44, 55, 46, 48, 50, 57, 52, 51, 55, 50, 53, 32, 51, 44, 49, 50, 32, 67, 51, 44, 49, 51, 46, 57, 48, 52, 50, 50, 49, 53, 32, 51, 46, 53, 57, 49, 51, 56, 50, 49, 51, 44, 49, 53, 46, 54, 55, 48, 51, 49, 56, 51, 32, 52, 46, 54, 48, 48, 53, 48, 51, 53, 56, 44, 49, 55, 46, 49, 50, 52, 54, 52, 55, 53, 32, 90, 32, 77, 49, 50, 44, 50, 51, 32, 67, 53, 46, 57, 50, 52, 56, 54, 55, 55, 53, 44, 50, 51, 32, 49, 44, 49, 56, 46, 48, 55, 53, 49, 51, 50, 50, 32, 49, 44, 49, 50, 32, 67, 49, 44, 53, 46, 57, 50, 52, 56, 54, 55, 55, 53, 32, 53, 46, 57, 50, 52, 56, 54, 55, 55, 53, 44, 49, 32, 49, 50, 44, 49, 32, 67, 49, 56, 46, 48, 55, 53, 49, 51, 50, 50, 44, 49, 32, 50, 51, 44, 53, 46, 57, 50, 52, 56, 54, 55, 55, 53, 32, 50, 51, 44, 49, 50, 32, 67, 50, 51, 44, 49, 56, 46, 48, 55, 53, 49, 51, 50, 50, 32, 49, 56, 46, 48, 55, 53, 49, 51, 50, 50, 44, 50, 51, 32, 49, 50, 44, 50, 51, 32, 90, 32, 77, 56, 44, 49, 48, 32, 67, 56, 44, 55, 46, 55, 53, 53, 55, 53, 57, 51, 54, 32, 57, 46, 53, 55, 57, 48, 57, 57, 53, 55, 44, 54, 32, 49, 50, 44, 54, 32, 67, 49, 52, 46, 52, 49, 52, 49, 57, 52, 56, 44, 54, 32, 49, 54, 44, 55, 46, 57, 50, 49, 53, 55, 56, 50, 49, 32, 49, 54, 44, 49, 48, 46, 50, 32, 67, 49, 54, 44, 49, 51, 46, 52, 55, 57, 54, 49, 52, 32, 49, 52, 46, 50, 49, 56, 48, 56, 54, 49, 44, 49, 53, 32, 49, 50, 44, 49, 53, 32, 67, 57, 46, 55, 54, 48, 56, 54, 51, 56, 50, 44, 49, 53, 32, 56, 44, 49, 51, 46, 52, 50, 55, 51, 55, 52, 51, 32, 56, 44, 49, 48, 32, 90, 32, 77, 49, 48, 44, 49, 48, 32, 67, 49, 48, 44, 49, 50, 46, 50, 54, 57, 50, 53, 54, 56, 32, 49, 48, 46, 56, 49, 56, 50, 49, 48, 56, 44, 49, 51, 32, 49, 50, 44, 49, 51, 32, 67, 49, 51, 46, 49, 55, 55, 55, 48, 54, 51, 44, 49, 51, 32, 49, 52, 44, 49, 50, 46, 50, 57, 56, 51, 57, 50, 55, 32, 49, 52, 44, 49, 48, 46, 50, 32, 67, 49, 52, 44, 56, 46, 57, 53, 48, 52, 49, 55, 51, 54, 32, 49, 51, 46, 50, 49, 53, 54, 53, 54, 56, 44, 56, 32, 49, 50, 44, 56, 32, 67, 49, 48, 46, 55, 51, 51, 55, 51, 56, 55, 44, 56, 32, 49, 48, 44, 56, 46, 56, 49, 53, 56, 50, 52, 55, 57, 32, 49, 48, 44, 49, 48, 32, 90, 34, 47, 62, 10, 60, 47, 115, 118, 103, 62, 10}} 16 | 17 | var resourceImageSvg = &fyne.StaticResource{ 18 | StaticName: "image.svg", 19 | StaticContent: []byte{ 20 | 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, 10, 32, 32, 60, 112, 97, 116, 104, 32, 102, 105, 108, 108, 45, 114, 117, 108, 101, 61, 34, 101, 118, 101, 110, 111, 100, 100, 34, 32, 100, 61, 34, 77, 52, 44, 49, 53, 46, 53, 56, 53, 55, 56, 54, 52, 32, 76, 56, 44, 49, 49, 46, 53, 56, 53, 55, 56, 54, 52, 32, 76, 49, 49, 46, 53, 44, 49, 53, 46, 48, 56, 53, 55, 56, 54, 52, 32, 76, 49, 56, 44, 56, 46, 53, 56, 53, 55, 56, 54, 52, 52, 32, 76, 50, 48, 44, 49, 48, 46, 53, 56, 53, 55, 56, 54, 52, 32, 76, 50, 48, 44, 52, 32, 76, 52, 44, 52, 32, 76, 52, 44, 49, 53, 46, 53, 56, 53, 55, 56, 54, 52, 32, 90, 32, 77, 52, 44, 49, 56, 46, 52, 49, 52, 50, 49, 51, 54, 32, 76, 52, 44, 50, 48, 32, 76, 49, 51, 46, 53, 56, 53, 55, 56, 54, 52, 44, 50, 48, 32, 76, 56, 44, 49, 52, 46, 52, 49, 52, 50, 49, 51, 54, 32, 76, 52, 44, 49, 56, 46, 52, 49, 52, 50, 49, 51, 54, 32, 90, 32, 77, 49, 54, 46, 52, 49, 52, 50, 49, 51, 54, 44, 50, 48, 32, 76, 50, 48, 44, 50, 48, 32, 76, 50, 48, 44, 49, 51, 46, 52, 49, 52, 50, 49, 51, 54, 32, 76, 49, 56, 44, 49, 49, 46, 52, 49, 52, 50, 49, 51, 54, 32, 76, 49, 50, 46, 57, 49, 52, 50, 49, 51, 54, 44, 49, 54, 46, 53, 32, 76, 49, 54, 46, 52, 49, 52, 50, 49, 51, 54, 44, 50, 48, 32, 90, 32, 77, 52, 44, 50, 32, 76, 50, 48, 44, 50, 32, 67, 50, 49, 46, 49, 48, 52, 53, 54, 57, 53, 44, 50, 32, 50, 50, 44, 50, 46, 56, 57, 53, 52, 51, 48, 53, 32, 50, 50, 44, 52, 32, 76, 50, 50, 44, 50, 48, 32, 67, 50, 50, 44, 50, 49, 46, 49, 48, 52, 53, 54, 57, 53, 32, 50, 49, 46, 49, 48, 52, 53, 54, 57, 53, 44, 50, 50, 32, 50, 48, 44, 50, 50, 32, 76, 52, 44, 50, 50, 32, 67, 50, 46, 56, 57, 53, 52, 51, 48, 53, 44, 50, 50, 32, 50, 44, 50, 49, 46, 49, 48, 52, 53, 54, 57, 53, 32, 50, 44, 50, 48, 32, 76, 50, 44, 52, 32, 67, 50, 44, 50, 46, 56, 57, 53, 52, 51, 48, 53, 32, 50, 46, 56, 57, 53, 52, 51, 48, 53, 44, 50, 32, 52, 44, 50, 32, 90, 32, 77, 49, 49, 44, 53, 32, 67, 49, 50, 46, 54, 53, 54, 56, 53, 52, 50, 44, 53, 32, 49, 52, 44, 54, 46, 51, 52, 51, 49, 52, 53, 55, 53, 32, 49, 52, 44, 56, 32, 67, 49, 52, 44, 57, 46, 54, 53, 54, 56, 53, 52, 50, 53, 32, 49, 50, 46, 54, 53, 54, 56, 53, 52, 50, 44, 49, 49, 32, 49, 49, 44, 49, 49, 32, 67, 57, 46, 51, 52, 51, 49, 52, 53, 55, 53, 44, 49, 49, 32, 56, 44, 57, 46, 54, 53, 54, 56, 53, 52, 50, 53, 32, 56, 44, 56, 32, 67, 56, 44, 54, 46, 51, 52, 51, 49, 52, 53, 55, 53, 32, 57, 46, 51, 52, 51, 49, 52, 53, 55, 53, 44, 53, 32, 49, 49, 44, 53, 32, 90, 32, 77, 49, 49, 44, 55, 32, 67, 49, 48, 46, 52, 52, 55, 55, 49, 53, 51, 44, 55, 32, 49, 48, 44, 55, 46, 52, 52, 55, 55, 49, 53, 50, 53, 32, 49, 48, 44, 56, 32, 67, 49, 48, 44, 56, 46, 53, 53, 50, 50, 56, 52, 55, 53, 32, 49, 48, 46, 52, 52, 55, 55, 49, 53, 51, 44, 57, 32, 49, 49, 44, 57, 32, 67, 49, 49, 46, 53, 53, 50, 50, 56, 52, 55, 44, 57, 32, 49, 50, 44, 56, 46, 53, 53, 50, 50, 56, 52, 55, 53, 32, 49, 50, 44, 56, 32, 67, 49, 50, 44, 55, 46, 52, 52, 55, 55, 49, 53, 50, 53, 32, 49, 49, 46, 53, 53, 50, 50, 56, 52, 55, 44, 55, 32, 49, 49, 44, 55, 32, 90, 34, 47, 62, 10, 60, 47, 115, 118, 103, 62, 10}} 21 | 22 | var resourceOpenSvg = &fyne.StaticResource{ 23 | StaticName: "open.svg", 24 | StaticContent: []byte{ 25 | 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, 10, 32, 32, 60, 112, 97, 116, 104, 32, 102, 105, 108, 108, 45, 114, 117, 108, 101, 61, 34, 101, 118, 101, 110, 111, 100, 100, 34, 32, 100, 61, 34, 77, 50, 49, 44, 56, 32, 67, 50, 50, 46, 49, 48, 52, 53, 54, 57, 53, 44, 56, 32, 50, 51, 44, 56, 46, 56, 57, 53, 52, 51, 48, 53, 32, 50, 51, 44, 49, 48, 32, 76, 50, 50, 46, 57, 55, 54, 49, 56, 55, 49, 44, 49, 48, 46, 50, 49, 54, 57, 51, 48, 53, 32, 76, 50, 48, 46, 57, 57, 53, 57, 55, 56, 50, 44, 49, 57, 46, 49, 50, 55, 56, 55, 48, 52, 32, 67, 50, 48, 46, 57, 51, 48, 48, 52, 50, 57, 44, 50, 48, 46, 49, 55, 50, 56, 53, 54, 51, 32, 50, 48, 46, 48, 54, 49, 54, 49, 54, 51, 44, 50, 49, 32, 49, 57, 44, 50, 49, 32, 76, 51, 44, 50, 49, 32, 67, 49, 46, 56, 57, 53, 52, 51, 48, 53, 44, 50, 49, 32, 49, 44, 50, 48, 46, 49, 48, 52, 53, 54, 57, 53, 32, 49, 44, 49, 57, 32, 76, 49, 44, 53, 32, 67, 49, 44, 51, 46, 56, 57, 53, 52, 51, 48, 53, 32, 49, 46, 56, 57, 53, 52, 51, 48, 53, 44, 51, 32, 51, 44, 51, 32, 76, 57, 44, 51, 32, 67, 49, 48, 46, 49, 50, 48, 48, 48, 50, 51, 44, 51, 32, 49, 48, 46, 56, 51, 50, 57, 51, 57, 44, 51, 46, 52, 55, 53, 52, 53, 49, 49, 56, 32, 49, 49, 46, 53, 52, 56, 57, 55, 54, 52, 44, 52, 46, 51, 55, 56, 56, 53, 51, 48, 57, 32, 67, 49, 49, 46, 53, 57, 54, 55, 53, 52, 55, 44, 52, 46, 52, 51, 57, 49, 51, 51, 53, 50, 32, 49, 49, 46, 56, 49, 48, 48, 57, 57, 57, 44, 52, 46, 55, 49, 53, 56, 56, 50, 55, 53, 32, 49, 49, 46, 56, 54, 50, 52, 56, 51, 49, 44, 52, 46, 55, 56, 48, 56, 49, 57, 52, 53, 32, 67, 49, 50, 46, 48, 49, 57, 55, 50, 54, 44, 52, 46, 57, 55, 53, 55, 52, 52, 57, 53, 32, 49, 50, 46, 48, 53, 49, 55, 55, 57, 53, 44, 52, 46, 57, 57, 57, 55, 50, 57, 53, 54, 32, 49, 50, 46, 48, 48, 49, 55, 56, 54, 51, 44, 53, 32, 76, 49, 57, 44, 53, 32, 67, 50, 48, 46, 49, 48, 52, 53, 54, 57, 53, 44, 53, 32, 50, 49, 44, 53, 46, 56, 57, 53, 52, 51, 48, 53, 32, 50, 49, 44, 55, 32, 76, 50, 49, 44, 56, 32, 90, 32, 77, 49, 57, 44, 56, 32, 76, 49, 57, 44, 55, 32, 76, 49, 49, 46, 57, 57, 52, 54, 52, 54, 44, 54, 46, 57, 57, 57, 57, 56, 53, 54, 55, 32, 67, 49, 49, 46, 50, 55, 54, 52, 57, 49, 53, 44, 54, 46, 57, 57, 54, 49, 52, 48, 53, 56, 32, 49, 48, 46, 56, 48, 56, 54, 57, 49, 54, 44, 54, 46, 54, 53, 57, 57, 48, 57, 50, 51, 32, 49, 48, 46, 51, 48, 53, 56, 51, 50, 50, 44, 54, 46, 48, 51, 54, 53, 52, 49, 52, 54, 32, 67, 49, 48, 46, 50, 51, 54, 52, 50, 56, 49, 44, 53, 46, 57, 53, 48, 53, 48, 52, 57, 55, 32, 49, 48, 46, 48, 49, 53, 56, 55, 51, 55, 44, 53, 46, 54, 54, 52, 52, 48, 51, 57, 56, 32, 57, 46, 57, 56, 49, 53, 57, 55, 55, 56, 44, 53, 46, 54, 50, 49, 49, 53, 57, 49, 54, 32, 67, 57, 46, 54, 48, 55, 48, 50, 49, 53, 56, 44, 53, 46, 49, 52, 56, 53, 54, 56, 49, 49, 32, 57, 46, 51, 56, 52, 50, 52, 52, 52, 50, 44, 53, 32, 57, 44, 53, 32, 76, 51, 44, 53, 32, 76, 51, 44, 57, 46, 56, 57, 49, 52, 53, 56, 54, 57, 32, 76, 51, 46, 48, 50, 57, 49, 50, 55, 48, 55, 44, 57, 46, 55, 54, 48, 52, 48, 53, 48, 50, 32, 67, 51, 46, 51, 49, 48, 50, 48, 48, 48, 54, 44, 56, 46, 54, 50, 49, 52, 53, 55, 50, 54, 32, 51, 46, 56, 51, 51, 51, 57, 53, 56, 55, 44, 56, 32, 53, 44, 56, 32, 76, 49, 57, 44, 56, 32, 90, 32, 77, 51, 46, 48, 50, 52, 51, 57, 51, 56, 51, 44, 49, 57, 32, 76, 49, 57, 44, 49, 57, 32, 76, 49, 57, 46, 48, 50, 51, 56, 49, 50, 57, 44, 49, 56, 46, 55, 56, 51, 48, 54, 57, 53, 32, 76, 50, 48, 46, 57, 55, 53, 54, 48, 54, 50, 44, 49, 48, 32, 76, 53, 46, 48, 51, 54, 51, 48, 53, 55, 56, 44, 49, 48, 32, 67, 53, 46, 48, 50, 48, 54, 56, 48, 55, 50, 44, 49, 48, 46, 48, 52, 57, 53, 53, 48, 49, 32, 52, 46, 57, 57, 57, 57, 49, 57, 49, 55, 44, 49, 48, 46, 49, 50, 49, 50, 57, 52, 55, 32, 52, 46, 57, 55, 54, 49, 56, 55, 48, 54, 44, 49, 48, 46, 50, 49, 54, 57, 51, 48, 53, 32, 76, 51, 46, 48, 50, 52, 51, 57, 51, 56, 51, 44, 49, 57, 32, 90, 34, 47, 62, 10, 60, 47, 115, 118, 103, 62, 10}} 26 | -------------------------------------------------------------------------------- /credits.go: -------------------------------------------------------------------------------- 1 | // Code generated by github.com/lusingander/fyne-credits-generator (v0.3.0); DO NOT EDIT. 2 | 3 | package main 4 | 5 | import ( 6 | "net/url" 7 | "strings" 8 | 9 | "fyne.io/fyne" 10 | "fyne.io/fyne/layout" 11 | "fyne.io/fyne/widget" 12 | ) 13 | 14 | // CreditsWindow returns a window displaying a list of licenses. 15 | func CreditsWindow(app fyne.App, size fyne.Size) fyne.Window { 16 | w := app.NewWindow("CREDITS") 17 | w.Resize(size) 18 | w.SetContent(CreditsContainer()) 19 | return w 20 | } 21 | 22 | // CreditsContainer returns a container displaying a list of licenses. 23 | func CreditsContainer() fyne.CanvasObject { 24 | nameLabel := widget.NewLabel("") 25 | urlLabel := widget.NewHyperlink("", nil) 26 | header := widget.NewVBox(nameLabel, urlLabel) 27 | entry := widget.NewMultiLineEntry() 28 | entry.Wrapping = fyne.TextWrapBreak 29 | width := 0 30 | for _, c := range credits { 31 | l := len(c.name) 32 | if l > width { 33 | width = l 34 | } 35 | } 36 | list := widget.NewList( 37 | func() int { 38 | return len(credits) 39 | }, 40 | func() fyne.CanvasObject { 41 | dummy := strings.Repeat("*", width) 42 | label := widget.NewLabel(dummy) 43 | label.Alignment = fyne.TextAlignCenter 44 | return label 45 | }, 46 | func(id widget.ListItemID, item fyne.CanvasObject) { 47 | item.(*widget.Label).SetText(credits[id].name) 48 | }, 49 | ) 50 | list.OnSelected = func(id widget.ListItemID) { 51 | c := credits[id] 52 | nameLabel.SetText(c.name) 53 | u, _ := url.Parse(c.url) 54 | urlLabel.SetText(c.url) 55 | urlLabel.SetURL(u) 56 | entry.SetText(c.text) 57 | } 58 | list.Select(0) 59 | text := widget.NewScrollContainer(entry) 60 | license := fyne.NewContainerWithLayout( 61 | layout.NewBorderLayout(header, nil, nil, nil), 62 | header, text, 63 | ) 64 | splitContainer := widget.NewHSplitContainer(list, license) 65 | splitContainer.SetOffset(0) 66 | return splitContainer 67 | } 68 | 69 | type credit struct { 70 | name, url, text string 71 | } 72 | 73 | var credits = []*credit{ 74 | { 75 | "Go (the standard library)", 76 | "https://golang.org/", 77 | ` 78 | Copyright (c) 2009 The Go Authors. All rights reserved. 79 | 80 | Redistribution and use in source and binary forms, with or without 81 | modification, are permitted provided that the following conditions are 82 | met: 83 | 84 | * Redistributions of source code must retain the above copyright 85 | notice, this list of conditions and the following disclaimer. 86 | * Redistributions in binary form must reproduce the above 87 | copyright notice, this list of conditions and the following disclaimer 88 | in the documentation and/or other materials provided with the 89 | distribution. 90 | * Neither the name of Google Inc. nor the names of its 91 | contributors may be used to endorse or promote products derived from 92 | this software without specific prior written permission. 93 | 94 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 95 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 96 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 97 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 98 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 99 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 100 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 101 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 102 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 103 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 104 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 105 | 106 | `, 107 | }, 108 | { 109 | "fyne.io/fyne", 110 | "https://fyne.io/fyne", 111 | ` 112 | BSD 3-Clause License 113 | 114 | Copyright (C) 2018 Fyne.io developers (see AUTHORS) 115 | All rights reserved. 116 | 117 | 118 | Redistribution and use in source and binary forms, with or without 119 | modification, are permitted provided that the following conditions are met: 120 | * Redistributions of source code must retain the above copyright 121 | notice, this list of conditions and the following disclaimer. 122 | * Redistributions in binary form must reproduce the above copyright 123 | notice, this list of conditions and the following disclaimer in the 124 | documentation and/or other materials provided with the distribution. 125 | * Neither the name of Fyne.io nor the names of its contributors may be 126 | used to endorse or promote products derived from this software without 127 | specific prior written permission. 128 | 129 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 130 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 131 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 132 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY 133 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 134 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 135 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 136 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 137 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 138 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 139 | 140 | 141 | `, 142 | }, 143 | { 144 | "github.com/davecgh/go-spew", 145 | "https://github.com/davecgh/go-spew", 146 | ` 147 | ISC License 148 | 149 | Copyright (c) 2012-2016 Dave Collins 150 | 151 | Permission to use, copy, modify, and/or distribute this software for any 152 | purpose with or without fee is hereby granted, provided that the above 153 | copyright notice and this permission notice appear in all copies. 154 | 155 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 156 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 157 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 158 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 159 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 160 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 161 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 162 | 163 | `, 164 | }, 165 | { 166 | "github.com/dustin/go-humanize", 167 | "https://github.com/dustin/go-humanize", 168 | ` 169 | Copyright (c) 2005-2008 Dustin Sallings 170 | 171 | Permission is hereby granted, free of charge, to any person obtaining a copy 172 | of this software and associated documentation files (the "Software"), to deal 173 | in the Software without restriction, including without limitation the rights 174 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 175 | copies of the Software, and to permit persons to whom the Software is 176 | furnished to do so, subject to the following conditions: 177 | 178 | The above copyright notice and this permission notice shall be included in 179 | all copies or substantial portions of the Software. 180 | 181 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 182 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 183 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 184 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 185 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 186 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 187 | SOFTWARE. 188 | 189 | 190 | 191 | `, 192 | }, 193 | { 194 | "github.com/fsnotify/fsnotify", 195 | "https://github.com/fsnotify/fsnotify", 196 | ` 197 | Copyright (c) 2012 The Go Authors. All rights reserved. 198 | Copyright (c) 2012-2019 fsnotify Authors. All rights reserved. 199 | 200 | Redistribution and use in source and binary forms, with or without 201 | modification, are permitted provided that the following conditions are 202 | met: 203 | 204 | * Redistributions of source code must retain the above copyright 205 | notice, this list of conditions and the following disclaimer. 206 | * Redistributions in binary form must reproduce the above 207 | copyright notice, this list of conditions and the following disclaimer 208 | in the documentation and/or other materials provided with the 209 | distribution. 210 | * Neither the name of Google Inc. nor the names of its 211 | contributors may be used to endorse or promote products derived from 212 | this software without specific prior written permission. 213 | 214 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 215 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 216 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 217 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 218 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 219 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 220 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 221 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 222 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 223 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 224 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 225 | 226 | `, 227 | }, 228 | { 229 | "github.com/fyne-io/mobile", 230 | "https://github.com/fyne-io/mobile", 231 | ` 232 | Copyright (c) 2009 The Go Authors. All rights reserved. 233 | 234 | Redistribution and use in source and binary forms, with or without 235 | modification, are permitted provided that the following conditions are 236 | met: 237 | 238 | * Redistributions of source code must retain the above copyright 239 | notice, this list of conditions and the following disclaimer. 240 | * Redistributions in binary form must reproduce the above 241 | copyright notice, this list of conditions and the following disclaimer 242 | in the documentation and/or other materials provided with the 243 | distribution. 244 | * Neither the name of Google Inc. nor the names of its 245 | contributors may be used to endorse or promote products derived from 246 | this software without specific prior written permission. 247 | 248 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 249 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 250 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 251 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 252 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 253 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 254 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 255 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 256 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 257 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 258 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 259 | 260 | `, 261 | }, 262 | { 263 | "github.com/go-gl/gl", 264 | "https://github.com/go-gl/gl", 265 | ` 266 | The MIT License (MIT) 267 | 268 | Copyright (c) 2014 Eric Woroshow 269 | 270 | Permission is hereby granted, free of charge, to any person obtaining a copy 271 | of this software and associated documentation files (the "Software"), to deal 272 | in the Software without restriction, including without limitation the rights 273 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 274 | copies of the Software, and to permit persons to whom the Software is 275 | furnished to do so, subject to the following conditions: 276 | 277 | The above copyright notice and this permission notice shall be included in all 278 | copies or substantial portions of the Software. 279 | 280 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 281 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 282 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 283 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 284 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 285 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 286 | SOFTWARE. 287 | `, 288 | }, 289 | { 290 | "github.com/go-gl/glfw/v3.3/glfw", 291 | "https://github.com/go-gl/glfw/v3.3/glfw", 292 | ` 293 | Copyright (c) 2012 The glfw3-go Authors. All rights reserved. 294 | 295 | Redistribution and use in source and binary forms, with or without 296 | modification, are permitted provided that the following conditions are 297 | met: 298 | 299 | * Redistributions of source code must retain the above copyright 300 | notice, this list of conditions and the following disclaimer. 301 | * Redistributions in binary form must reproduce the above 302 | copyright notice, this list of conditions and the following disclaimer 303 | in the documentation and/or other materials provided with the 304 | distribution. 305 | * Neither the name of Google Inc. nor the names of its 306 | contributors may be used to endorse or promote products derived from 307 | this software without specific prior written permission. 308 | 309 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 310 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 311 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 312 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 313 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 314 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 315 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 316 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 317 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 318 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 319 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 320 | 321 | `, 322 | }, 323 | { 324 | "github.com/godbus/dbus/v5", 325 | "https://github.com/godbus/dbus/v5", 326 | ` 327 | Copyright (c) 2013, Georg Reinke (), Google 328 | All rights reserved. 329 | 330 | Redistribution and use in source and binary forms, with or without 331 | modification, are permitted provided that the following conditions 332 | are met: 333 | 334 | 1. Redistributions of source code must retain the above copyright notice, 335 | this list of conditions and the following disclaimer. 336 | 337 | 2. Redistributions in binary form must reproduce the above copyright 338 | notice, this list of conditions and the following disclaimer in the 339 | documentation and/or other materials provided with the distribution. 340 | 341 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 342 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 343 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 344 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 345 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 346 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 347 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 348 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 349 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 350 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 351 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 352 | 353 | `, 354 | }, 355 | { 356 | "github.com/goki/freetype", 357 | "https://github.com/goki/freetype", 358 | ` 359 | Use of the Freetype-Go software is subject to your choice of exactly one of 360 | the following two licenses: 361 | * The FreeType License, which is similar to the original BSD license with 362 | an advertising clause, or 363 | * The GNU General Public License (GPL), version 2 or later. 364 | 365 | The text of these licenses are available in the licenses/ftl.txt and the 366 | licenses/gpl.txt files respectively. They are also available at 367 | http://freetype.sourceforge.net/license.html 368 | 369 | The Luxi fonts in the testdata directory are licensed separately. See the 370 | testdata/COPYING file for details. 371 | 372 | `, 373 | }, 374 | { 375 | "github.com/kr/text", 376 | "https://github.com/kr/text", 377 | ` 378 | Copyright 2012 Keith Rarick 379 | 380 | Permission is hereby granted, free of charge, to any person obtaining a copy 381 | of this software and associated documentation files (the "Software"), to deal 382 | in the Software without restriction, including without limitation the rights 383 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 384 | copies of the Software, and to permit persons to whom the Software is 385 | furnished to do so, subject to the following conditions: 386 | 387 | The above copyright notice and this permission notice shall be included in 388 | all copies or substantial portions of the Software. 389 | 390 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 391 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 392 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 393 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 394 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 395 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 396 | THE SOFTWARE. 397 | 398 | `, 399 | }, 400 | { 401 | "github.com/nfnt/resize", 402 | "https://github.com/nfnt/resize", 403 | ` 404 | Copyright (c) 2012, Jan Schlicht 405 | 406 | Permission to use, copy, modify, and/or distribute this software for any purpose 407 | with or without fee is hereby granted, provided that the above copyright notice 408 | and this permission notice appear in all copies. 409 | 410 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 411 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 412 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 413 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 414 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 415 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 416 | THIS SOFTWARE. 417 | 418 | `, 419 | }, 420 | { 421 | "github.com/niemeyer/pretty", 422 | "https://github.com/niemeyer/pretty", 423 | ` 424 | Copyright 2012 Keith Rarick 425 | 426 | Permission is hereby granted, free of charge, to any person obtaining a copy 427 | of this software and associated documentation files (the "Software"), to deal 428 | in the Software without restriction, including without limitation the rights 429 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 430 | copies of the Software, and to permit persons to whom the Software is 431 | furnished to do so, subject to the following conditions: 432 | 433 | The above copyright notice and this permission notice shall be included in 434 | all copies or substantial portions of the Software. 435 | 436 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 437 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 438 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 439 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 440 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 441 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 442 | THE SOFTWARE. 443 | 444 | `, 445 | }, 446 | { 447 | "github.com/pmezard/go-difflib", 448 | "https://github.com/pmezard/go-difflib", 449 | ` 450 | Copyright (c) 2013, Patrick Mezard 451 | All rights reserved. 452 | 453 | Redistribution and use in source and binary forms, with or without 454 | modification, are permitted provided that the following conditions are 455 | met: 456 | 457 | Redistributions of source code must retain the above copyright 458 | notice, this list of conditions and the following disclaimer. 459 | Redistributions in binary form must reproduce the above copyright 460 | notice, this list of conditions and the following disclaimer in the 461 | documentation and/or other materials provided with the distribution. 462 | The names of its contributors may not be used to endorse or promote 463 | products derived from this software without specific prior written 464 | permission. 465 | 466 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 467 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 468 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 469 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 470 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 471 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 472 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 473 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 474 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 475 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 476 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 477 | 478 | `, 479 | }, 480 | { 481 | "github.com/srwiley/oksvg", 482 | "https://github.com/srwiley/oksvg", 483 | ` 484 | BSD 3-Clause License 485 | 486 | Copyright (c) 2018, Steven R Wiley 487 | All rights reserved. 488 | 489 | Redistribution and use in source and binary forms, with or without 490 | modification, are permitted provided that the following conditions are met: 491 | 492 | * Redistributions of source code must retain the above copyright notice, this 493 | list of conditions and the following disclaimer. 494 | 495 | * Redistributions in binary form must reproduce the above copyright notice, 496 | this list of conditions and the following disclaimer in the documentation 497 | and/or other materials provided with the distribution. 498 | 499 | * Neither the name of the copyright holder nor the names of its 500 | contributors may be used to endorse or promote products derived from 501 | this software without specific prior written permission. 502 | 503 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 504 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 505 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 506 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 507 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 508 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 509 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 510 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 511 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 512 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 513 | 514 | `, 515 | }, 516 | { 517 | "github.com/srwiley/rasterx", 518 | "https://github.com/srwiley/rasterx", 519 | ` 520 | BSD 3-Clause License 521 | 522 | Copyright (c) 2018, Steven R Wiley 523 | All rights reserved. 524 | 525 | Redistribution and use in source and binary forms, with or without 526 | modification, are permitted provided that the following conditions are met: 527 | 528 | * Redistributions of source code must retain the above copyright notice, this 529 | list of conditions and the following disclaimer. 530 | 531 | * Redistributions in binary form must reproduce the above copyright notice, 532 | this list of conditions and the following disclaimer in the documentation 533 | and/or other materials provided with the distribution. 534 | 535 | * Neither the name of the copyright holder nor the names of its 536 | contributors may be used to endorse or promote products derived from 537 | this software without specific prior written permission. 538 | 539 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 540 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 541 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 542 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 543 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 544 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 545 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 546 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 547 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 548 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 549 | 550 | `, 551 | }, 552 | { 553 | "github.com/stretchr/testify", 554 | "https://github.com/stretchr/testify", 555 | ` 556 | MIT License 557 | 558 | Copyright (c) 2012-2018 Mat Ryer and Tyler Bunnell 559 | 560 | Permission is hereby granted, free of charge, to any person obtaining a copy 561 | of this software and associated documentation files (the "Software"), to deal 562 | in the Software without restriction, including without limitation the rights 563 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 564 | copies of the Software, and to permit persons to whom the Software is 565 | furnished to do so, subject to the following conditions: 566 | 567 | The above copyright notice and this permission notice shall be included in all 568 | copies or substantial portions of the Software. 569 | 570 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 571 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 572 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 573 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 574 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 575 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 576 | SOFTWARE. 577 | 578 | `, 579 | }, 580 | { 581 | "golang.org/x/image", 582 | "https://golang.org/x/image", 583 | ` 584 | Copyright (c) 2009 The Go Authors. All rights reserved. 585 | 586 | Redistribution and use in source and binary forms, with or without 587 | modification, are permitted provided that the following conditions are 588 | met: 589 | 590 | * Redistributions of source code must retain the above copyright 591 | notice, this list of conditions and the following disclaimer. 592 | * Redistributions in binary form must reproduce the above 593 | copyright notice, this list of conditions and the following disclaimer 594 | in the documentation and/or other materials provided with the 595 | distribution. 596 | * Neither the name of Google Inc. nor the names of its 597 | contributors may be used to endorse or promote products derived from 598 | this software without specific prior written permission. 599 | 600 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 601 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 602 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 603 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 604 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 605 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 606 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 607 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 608 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 609 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 610 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 611 | 612 | `, 613 | }, 614 | { 615 | "golang.org/x/net", 616 | "https://golang.org/x/net", 617 | ` 618 | Copyright (c) 2009 The Go Authors. All rights reserved. 619 | 620 | Redistribution and use in source and binary forms, with or without 621 | modification, are permitted provided that the following conditions are 622 | met: 623 | 624 | * Redistributions of source code must retain the above copyright 625 | notice, this list of conditions and the following disclaimer. 626 | * Redistributions in binary form must reproduce the above 627 | copyright notice, this list of conditions and the following disclaimer 628 | in the documentation and/or other materials provided with the 629 | distribution. 630 | * Neither the name of Google Inc. nor the names of its 631 | contributors may be used to endorse or promote products derived from 632 | this software without specific prior written permission. 633 | 634 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 635 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 636 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 637 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 638 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 639 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 640 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 641 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 642 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 643 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 644 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 645 | 646 | `, 647 | }, 648 | { 649 | "golang.org/x/sys", 650 | "https://golang.org/x/sys", 651 | ` 652 | Copyright (c) 2009 The Go Authors. All rights reserved. 653 | 654 | Redistribution and use in source and binary forms, with or without 655 | modification, are permitted provided that the following conditions are 656 | met: 657 | 658 | * Redistributions of source code must retain the above copyright 659 | notice, this list of conditions and the following disclaimer. 660 | * Redistributions in binary form must reproduce the above 661 | copyright notice, this list of conditions and the following disclaimer 662 | in the documentation and/or other materials provided with the 663 | distribution. 664 | * Neither the name of Google Inc. nor the names of its 665 | contributors may be used to endorse or promote products derived from 666 | this software without specific prior written permission. 667 | 668 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 669 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 670 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 671 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 672 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 673 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 674 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 675 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 676 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 677 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 678 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 679 | 680 | `, 681 | }, 682 | { 683 | "golang.org/x/text", 684 | "https://golang.org/x/text", 685 | ` 686 | Copyright (c) 2009 The Go Authors. All rights reserved. 687 | 688 | Redistribution and use in source and binary forms, with or without 689 | modification, are permitted provided that the following conditions are 690 | met: 691 | 692 | * Redistributions of source code must retain the above copyright 693 | notice, this list of conditions and the following disclaimer. 694 | * Redistributions in binary form must reproduce the above 695 | copyright notice, this list of conditions and the following disclaimer 696 | in the documentation and/or other materials provided with the 697 | distribution. 698 | * Neither the name of Google Inc. nor the names of its 699 | contributors may be used to endorse or promote products derived from 700 | this software without specific prior written permission. 701 | 702 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 703 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 704 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 705 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 706 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 707 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 708 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 709 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 710 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 711 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 712 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 713 | 714 | `, 715 | }, 716 | { 717 | "gopkg.in/check.v1", 718 | "https://gopkg.in/check.v1", 719 | ` 720 | Gocheck - A rich testing framework for Go 721 | 722 | Copyright (c) 2010-2013 Gustavo Niemeyer 723 | 724 | All rights reserved. 725 | 726 | Redistribution and use in source and binary forms, with or without 727 | modification, are permitted provided that the following conditions are met: 728 | 729 | 1. Redistributions of source code must retain the above copyright notice, this 730 | list of conditions and the following disclaimer. 731 | 2. Redistributions in binary form must reproduce the above copyright notice, 732 | this list of conditions and the following disclaimer in the documentation 733 | and/or other materials provided with the distribution. 734 | 735 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 736 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 737 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 738 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 739 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 740 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 741 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 742 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 743 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 744 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 745 | 746 | `, 747 | }, 748 | { 749 | "gopkg.in/yaml.v2", 750 | "https://gopkg.in/yaml.v2", 751 | ` 752 | Apache License 753 | Version 2.0, January 2004 754 | http://www.apache.org/licenses/ 755 | 756 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 757 | 758 | 1. Definitions. 759 | 760 | "License" shall mean the terms and conditions for use, reproduction, 761 | and distribution as defined by Sections 1 through 9 of this document. 762 | 763 | "Licensor" shall mean the copyright owner or entity authorized by 764 | the copyright owner that is granting the License. 765 | 766 | "Legal Entity" shall mean the union of the acting entity and all 767 | other entities that control, are controlled by, or are under common 768 | control with that entity. For the purposes of this definition, 769 | "control" means (i) the power, direct or indirect, to cause the 770 | direction or management of such entity, whether by contract or 771 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 772 | outstanding shares, or (iii) beneficial ownership of such entity. 773 | 774 | "You" (or "Your") shall mean an individual or Legal Entity 775 | exercising permissions granted by this License. 776 | 777 | "Source" form shall mean the preferred form for making modifications, 778 | including but not limited to software source code, documentation 779 | source, and configuration files. 780 | 781 | "Object" form shall mean any form resulting from mechanical 782 | transformation or translation of a Source form, including but 783 | not limited to compiled object code, generated documentation, 784 | and conversions to other media types. 785 | 786 | "Work" shall mean the work of authorship, whether in Source or 787 | Object form, made available under the License, as indicated by a 788 | copyright notice that is included in or attached to the work 789 | (an example is provided in the Appendix below). 790 | 791 | "Derivative Works" shall mean any work, whether in Source or Object 792 | form, that is based on (or derived from) the Work and for which the 793 | editorial revisions, annotations, elaborations, or other modifications 794 | represent, as a whole, an original work of authorship. For the purposes 795 | of this License, Derivative Works shall not include works that remain 796 | separable from, or merely link (or bind by name) to the interfaces of, 797 | the Work and Derivative Works thereof. 798 | 799 | "Contribution" shall mean any work of authorship, including 800 | the original version of the Work and any modifications or additions 801 | to that Work or Derivative Works thereof, that is intentionally 802 | submitted to Licensor for inclusion in the Work by the copyright owner 803 | or by an individual or Legal Entity authorized to submit on behalf of 804 | the copyright owner. For the purposes of this definition, "submitted" 805 | means any form of electronic, verbal, or written communication sent 806 | to the Licensor or its representatives, including but not limited to 807 | communication on electronic mailing lists, source code control systems, 808 | and issue tracking systems that are managed by, or on behalf of, the 809 | Licensor for the purpose of discussing and improving the Work, but 810 | excluding communication that is conspicuously marked or otherwise 811 | designated in writing by the copyright owner as "Not a Contribution." 812 | 813 | "Contributor" shall mean Licensor and any individual or Legal Entity 814 | on behalf of whom a Contribution has been received by Licensor and 815 | subsequently incorporated within the Work. 816 | 817 | 2. Grant of Copyright License. Subject to the terms and conditions of 818 | this License, each Contributor hereby grants to You a perpetual, 819 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 820 | copyright license to reproduce, prepare Derivative Works of, 821 | publicly display, publicly perform, sublicense, and distribute the 822 | Work and such Derivative Works in Source or Object form. 823 | 824 | 3. Grant of Patent License. Subject to the terms and conditions of 825 | this License, each Contributor hereby grants to You a perpetual, 826 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 827 | (except as stated in this section) patent license to make, have made, 828 | use, offer to sell, sell, import, and otherwise transfer the Work, 829 | where such license applies only to those patent claims licensable 830 | by such Contributor that are necessarily infringed by their 831 | Contribution(s) alone or by combination of their Contribution(s) 832 | with the Work to which such Contribution(s) was submitted. If You 833 | institute patent litigation against any entity (including a 834 | cross-claim or counterclaim in a lawsuit) alleging that the Work 835 | or a Contribution incorporated within the Work constitutes direct 836 | or contributory patent infringement, then any patent licenses 837 | granted to You under this License for that Work shall terminate 838 | as of the date such litigation is filed. 839 | 840 | 4. Redistribution. You may reproduce and distribute copies of the 841 | Work or Derivative Works thereof in any medium, with or without 842 | modifications, and in Source or Object form, provided that You 843 | meet the following conditions: 844 | 845 | (a) You must give any other recipients of the Work or 846 | Derivative Works a copy of this License; and 847 | 848 | (b) You must cause any modified files to carry prominent notices 849 | stating that You changed the files; and 850 | 851 | (c) You must retain, in the Source form of any Derivative Works 852 | that You distribute, all copyright, patent, trademark, and 853 | attribution notices from the Source form of the Work, 854 | excluding those notices that do not pertain to any part of 855 | the Derivative Works; and 856 | 857 | (d) If the Work includes a "NOTICE" text file as part of its 858 | distribution, then any Derivative Works that You distribute must 859 | include a readable copy of the attribution notices contained 860 | within such NOTICE file, excluding those notices that do not 861 | pertain to any part of the Derivative Works, in at least one 862 | of the following places: within a NOTICE text file distributed 863 | as part of the Derivative Works; within the Source form or 864 | documentation, if provided along with the Derivative Works; or, 865 | within a display generated by the Derivative Works, if and 866 | wherever such third-party notices normally appear. The contents 867 | of the NOTICE file are for informational purposes only and 868 | do not modify the License. You may add Your own attribution 869 | notices within Derivative Works that You distribute, alongside 870 | or as an addendum to the NOTICE text from the Work, provided 871 | that such additional attribution notices cannot be construed 872 | as modifying the License. 873 | 874 | You may add Your own copyright statement to Your modifications and 875 | may provide additional or different license terms and conditions 876 | for use, reproduction, or distribution of Your modifications, or 877 | for any such Derivative Works as a whole, provided Your use, 878 | reproduction, and distribution of the Work otherwise complies with 879 | the conditions stated in this License. 880 | 881 | 5. Submission of Contributions. Unless You explicitly state otherwise, 882 | any Contribution intentionally submitted for inclusion in the Work 883 | by You to the Licensor shall be under the terms and conditions of 884 | this License, without any additional terms or conditions. 885 | Notwithstanding the above, nothing herein shall supersede or modify 886 | the terms of any separate license agreement you may have executed 887 | with Licensor regarding such Contributions. 888 | 889 | 6. Trademarks. This License does not grant permission to use the trade 890 | names, trademarks, service marks, or product names of the Licensor, 891 | except as required for reasonable and customary use in describing the 892 | origin of the Work and reproducing the content of the NOTICE file. 893 | 894 | 7. Disclaimer of Warranty. Unless required by applicable law or 895 | agreed to in writing, Licensor provides the Work (and each 896 | Contributor provides its Contributions) on an "AS IS" BASIS, 897 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 898 | implied, including, without limitation, any warranties or conditions 899 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 900 | PARTICULAR PURPOSE. You are solely responsible for determining the 901 | appropriateness of using or redistributing the Work and assume any 902 | risks associated with Your exercise of permissions under this License. 903 | 904 | 8. Limitation of Liability. In no event and under no legal theory, 905 | whether in tort (including negligence), contract, or otherwise, 906 | unless required by applicable law (such as deliberate and grossly 907 | negligent acts) or agreed to in writing, shall any Contributor be 908 | liable to You for damages, including any direct, indirect, special, 909 | incidental, or consequential damages of any character arising as a 910 | result of this License or out of the use or inability to use the 911 | Work (including but not limited to damages for loss of goodwill, 912 | work stoppage, computer failure or malfunction, or any and all 913 | other commercial damages or losses), even if such Contributor 914 | has been advised of the possibility of such damages. 915 | 916 | 9. Accepting Warranty or Additional Liability. While redistributing 917 | the Work or Derivative Works thereof, You may choose to offer, 918 | and charge a fee for, acceptance of support, warranty, indemnity, 919 | or other liability obligations and/or rights consistent with this 920 | License. However, in accepting such obligations, You may act only 921 | on Your own behalf and on Your sole responsibility, not on behalf 922 | of any other Contributor, and only if You agree to indemnify, 923 | defend, and hold each Contributor harmless for any liability 924 | incurred by, or claims asserted against, such Contributor by reason 925 | of your accepting any such warranty or additional liability. 926 | 927 | END OF TERMS AND CONDITIONS 928 | 929 | APPENDIX: How to apply the Apache License to your work. 930 | 931 | To apply the Apache License to your work, attach the following 932 | boilerplate notice, with the fields enclosed by brackets "{}" 933 | replaced with your own identifying information. (Don't include 934 | the brackets!) The text should be enclosed in the appropriate 935 | comment syntax for the file format. We also recommend that a 936 | file or class name and description of purpose be included on the 937 | same "printed page" as the copyright notice for easier 938 | identification within third-party archives. 939 | 940 | Copyright {yyyy} {name of copyright owner} 941 | 942 | Licensed under the Apache License, Version 2.0 (the "License"); 943 | you may not use this file except in compliance with the License. 944 | You may obtain a copy of the License at 945 | 946 | http://www.apache.org/licenses/LICENSE-2.0 947 | 948 | Unless required by applicable law or agreed to in writing, software 949 | distributed under the License is distributed on an "AS IS" BASIS, 950 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 951 | See the License for the specific language governing permissions and 952 | limitations under the License. 953 | 954 | `, 955 | }, 956 | { 957 | "fyne-credits-generator", 958 | "https://github.com/lusingander/fyne-credits-generator", 959 | ` 960 | MIT License 961 | 962 | Copyright (c) 2020 lusingander 963 | 964 | Permission is hereby granted, free of charge, to any person obtaining a copy 965 | of this software and associated documentation files (the "Software"), to deal 966 | in the Software without restriction, including without limitation the rights 967 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 968 | copies of the Software, and to permit persons to whom the Software is 969 | furnished to do so, subject to the following conditions: 970 | 971 | The above copyright notice and this permission notice shall be included in all 972 | copies or substantial portions of the Software. 973 | 974 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 975 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 976 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 977 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 978 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 979 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 980 | SOFTWARE. 981 | `, 982 | }, 983 | } 984 | --------------------------------------------------------------------------------