Browse Source

学习 fyne 项目代码

master
VIVIMAN 3 years ago
parent
commit
1aa3148f80
  1. 4
      gui_fyne_learn/.gitignore
  2. 2
      gui_fyne_learn/AUTHORS
  3. BIN
      gui_fyne_learn/Icon.png
  4. 26
      gui_fyne_learn/LICENSE
  5. 72
      gui_fyne_learn/README.md
  6. 212
      gui_fyne_learn/bugs/board.go
  7. 111
      gui_fyne_learn/bugs/board_test.go
  8. 1
      gui_fyne_learn/bugs/bug.svg
  9. 20
      gui_fyne_learn/bugs/bundled.go
  10. 124
      gui_fyne_learn/bugs/button.go
  11. 1
      gui_fyne_learn/bugs/code.svg
  12. 1
      gui_fyne_learn/bugs/flag.svg
  13. 13
      gui_fyne_learn/bugs/gen.sh
  14. 213
      gui_fyne_learn/bugs/main.go
  15. 132
      gui_fyne_learn/clock/clock.go
  16. 10
      gui_fyne_learn/data.go
  17. 123
      gui_fyne_learn/fractal/main.go
  18. 10
      gui_fyne_learn/go.mod
  19. 93
      gui_fyne_learn/go.sum
  20. BIN
      gui_fyne_learn/img/bugs.png
  21. BIN
      gui_fyne_learn/img/clock-dark.png
  22. BIN
      gui_fyne_learn/img/clock-light.png
  23. BIN
      gui_fyne_learn/img/fractal.png
  24. BIN
      gui_fyne_learn/img/icon/bug.png
  25. 27
      gui_fyne_learn/img/icon/bundled.go
  26. BIN
      gui_fyne_learn/img/icon/clock.png
  27. BIN
      gui_fyne_learn/img/icon/fractal.png
  28. 14
      gui_fyne_learn/img/icon/gen.sh
  29. 13
      gui_fyne_learn/img/icon/icon.go
  30. BIN
      gui_fyne_learn/img/icon/xkcd.png
  31. BIN
      gui_fyne_learn/img/xkcd.png
  32. 71
      gui_fyne_learn/main.go
  33. 101
      gui_fyne_learn/tictactoe/board.go
  34. 30
      gui_fyne_learn/tictactoe/grid.go
  35. 166
      gui_fyne_learn/xkcd/main.go

4
gui_fyne_learn/.gitignore

@ -0,0 +1,4 @@
.idea
.DS_Store
examples

2
gui_fyne_learn/AUTHORS

@ -0,0 +1,2 @@
Andy Williams <andy@andy.xyz>
Steve OConnor <steveoc64@gmail.com>

BIN
gui_fyne_learn/Icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

26
gui_fyne_learn/LICENSE

@ -0,0 +1,26 @@
Copyright (C) 2018-2020 Fyne.io developers (see AUTHORS)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Fyne.io nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

72
gui_fyne_learn/README.md

@ -0,0 +1,72 @@
<p align="center">
<a href="https://goreportcard.com/report/github.com/fyne-io/examples"><img src="https://goreportcard.com/badge/github.com/fyne-io/examples" alt="Code Status" /></a>
<a href='http://gophers.slack.com/messages/fyne'><img src='https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=blue' alt='Join us on Slack' /></a>
<a href='https://fossfi.sh/support-fyneio'><img src='https://img.shields.io/badge/$-support_us-orange.svg?labelWidth=20&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAkCAYAAADPRbkKAAAABmJLR0QA7wAyAD/CTveyAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAAB3RJTUUH4wMVCQ4LeuPReAAABDFJREFUWMPVmX9oVWUYxz/3tr5jbX9oYiZWSmNgoYYlUVqraYvIamlCg8pqUBTZr38qMhZZ0A8oIqKIiLJoJtEIsR+zki1mGqQwZTYkImtlgk5Wy7jPnbv90XPgcD3n3Lvdy731wOGc+77nec73Oe/zfp/nOTeVy+WolphZC3CVH9N9OAsMAzuBLkkHkmykKu2AmQGsAjYAC4pQ+QJ4WNJg1R0ws3pgI3DTJFVPAM8AGyRNVMUBB98DLCvBzLtAh6QTwUC6gmGzuUTwAGuBR8MD6QpFzwPAypi5v4HXgRXAEg+vLQm2njSzCyoWQmbWAPwAzIqY/gm4UdJAhN5qoAuojdCbAD4BVlViBdbGgJ8A2gPwZtZsZvea2XwASd1AZ4zNNHA90FgJB9ri6FHStw7+WaAPeA3YbWaBzovAiF/nfMUC+QD4sRIOLIkZ73Hws4BHQuOneY7A2aY/CHdgL3AtsBhYD9TVVMCB0+O2h59nRJBJfej6WOj6Bj8C2VcpFoqSC/38PbArb24zQDabBWhOsFFbNAuZ2UygxZcvcHwPsEPScILecaAuYmoUaJR01MymAU8AFzm7vCxp3MxagW0xpv8EzinogLPC40A7cGpMKHQB6yX9FqE/ACyKMb8VWC0pG6E3D+gF5sbo9km6Ml0A/N3Ad8BtMeABBNwB9JrZ7Ij5PQmPuA7YZWZXmFnan1ljZrf65p2boDuQmInNrAN4I29DJUmT02C+bCtiL/QCo2Y2BIwB7wFzCuj1xGZiM7vcb9gOTAMunUTZMVPSkZCtJuBAmQngIHCupImaCPApoBVokvSrj7UDm4o0Pg84Evo97EkoVUYHXgjK6pNWwB1I5dfdZtbtjUghWSppZ0ivDvirjA4MAosCfCeFhaRcPvhQLV6QbYGhvLHZZQRvwO1hfJNJZFu9qkySNyUdyxu7pkzgc8BDknZPuSMzs8XA58AZEdPbgTZJY3ld2GABOixWXpH0YMk9sZlN96zZ6hn2F+AdYFM4IZlZLfBRQiMzGXlaUmdFmnpPSC3A814alCJjwDpJG+NuqCkC0HzgLi+L651RvgTeAu4ELnHazAJnedkwpwzvoh+4RdLPid+FMpnMDElHY8CvAd4GGiKmR4A1DvwpYHkZabLTO7KCkgYeiwG/3Iu0hoQ6fwvwh6QVwFKn2tEpgB4BPgSaJS0oFnywAnuB5yR15bHNV6HPfUnyqaSVIV0BlwEXe51zNtAYehGHgMNOyfu9F+iXND6V5UplMplXgfuAj4HPgIXAPcXsj5CcKelwNbqiVCaTOd/jrhRZJumbajiQlrQf6OZ/KkEpsQ74vQQ7w1V1QNIh4Gbg+BRs9BXi6kqsAJK+Bq52hihWxoH7/wshFDixw786vF9kmm+TtK+aDsTWQma2EOjg379/zgNO8akh73NfknSw2pv4H3Ayg0FmbTMRAAAAAElFTkSuQmCC' alt='Support Fyne.io' /></a>
<br />
<a href="https://travis-ci.com/github/fyne-io/examples"><img src="https://travis-ci.com/github/fyne-io/examples.svg" alt="Build Status" /></a>
<a href='https://coveralls.io/github/fyne-io/examples?branch=develop'><img src='https://coveralls.io/repos/github/fyne-io/examples/badge.svg?branch=develop' alt='Coverage Status' /></a>
</p>
# Fyne Examples
Here we will gather example apps that use the [Fyne](http://fyne.io) toolkit.
You can start the main example app that links to all the others by running
```bash
go run .
```
All these examples are fully scalable - try setting the `FYNE_SCALE`
environment variable to override the detection of your screen's density.
Many also respond to the current theme (this is default behaviour for
apps built using Fyne widgets) - you can try setting `FYNE_THEME=light`
to change from the default dark theme.
## Widget based examples
The following examples use mostly built in widgets making applications
trivial to build :).
### Calculator
Moved to [calculator repository](https://github.com/fyne-io/calculator/)
### Bugs game (like MineSweeper)
Hunt the squares to reveal everything apart from the bugs!
![](img/bugs.png)
### XKCD
An XKCD comic browser with random and lookup features.
![](img/xkcd.png)
## Graphics based examples
These examples use the Fyne canvas API to draw primitive shapes,
text and images to create custom user interfaces.
### Clock
A simple analog clock that matches the current theme.
![](img/clock-dark.png) &nbsp; ![](img/clock-light.png)
### Fractal
A fractal viewer that can be panned and zoomed
![](img/fractal.png)
### Solitaire
Moved to [solitaire repository](https://github.com/fyne-io/solitaire/)
### Life
Moved to [life repository](https://github.com/fyne-io/life/)

212
gui_fyne_learn/bugs/board.go

@ -0,0 +1,212 @@
package bugs
import (
"fmt"
"math/rand"
"strings"
"time"
)
type square struct {
bug bool
shown bool
flagged bool
near int
}
type board struct {
height, width int
win, lose func()
bugCount int
flagCount int
bugs [][]square
}
func (b *board) countHidden() int {
count := 0
for y := 0; y < b.height; y++ {
for x := 0; x < b.width; x++ {
item := b.bugs[y][x]
if !item.shown {
count++
}
}
}
return count
}
func (b *board) load(count int) {
b.bugCount = count
b.flagCount = 0
if b.bugs == nil {
b.bugs = make([][]square, b.height)
for y := 0; y < b.height; y++ {
b.bugs[y] = make([]square, b.width)
}
} else {
for y := 0; y < b.height; y++ {
for x := 0; x < b.width; x++ {
b.bugs[y][x].shown = false
b.bugs[y][x].bug = false
b.bugs[y][x].flagged = false
b.bugs[y][x].near = 0
}
}
}
for i := 0; i < count; i++ {
x := rand.Intn(b.width)
y := rand.Intn(b.height)
if b.bugs[y][x].bug {
i--
} else {
b.setMine(x, y)
}
}
}
func (b *board) incSquare(x, y int) {
if x < 0 || y < 0 {
return
}
if x >= b.width || y >= b.height {
return
}
if b.bugs[y][x].bug {
return
}
b.bugs[y][x].near++
}
func (b *board) setMine(x, y int) {
if b.bugs[y][x].bug {
return
}
b.bugs[y][x].bug = true
b.incSquare(x-1, y-1)
b.incSquare(x, y-1)
b.incSquare(x+1, y-1)
b.incSquare(x-1, y)
b.incSquare(x+1, y)
b.incSquare(x-1, y+1)
b.incSquare(x, y+1)
b.incSquare(x+1, y+1)
}
func (b *board) reveal(x, y int) {
if x < 0 || y < 0 {
return
}
if x >= b.width || y >= b.height {
return
}
sq := b.bugs[y][x]
if sq.shown || sq.flagged {
return
}
b.bugs[y][x].shown = true
if sq.bug {
if b.lose != nil {
b.lose()
}
return
}
if sq.near == 0 {
b.reveal(x-1, y-1)
b.reveal(x, y-1)
b.reveal(x+1, y-1)
b.reveal(x-1, y)
b.reveal(x+1, y)
b.reveal(x-1, y+1)
b.reveal(x, y+1)
b.reveal(x+1, y+1)
}
if b.countHidden() == b.bugCount && b.win != nil {
b.win()
}
}
func (b *board) flag(x, y int) {
if x < 0 || y < 0 {
return
}
if x >= b.width || y >= b.height {
return
}
sq := b.bugs[y][x]
if sq.shown {
return
}
if sq.flagged {
b.bugs[y][x].flagged = false
b.flagCount--
} else {
b.bugs[y][x].flagged = true
b.flagCount++
}
}
func (b *board) flagged(x, y int) bool {
if x < 0 || y < 0 {
return false
}
if x >= b.width || y >= b.height {
return false
}
sq := b.bugs[y][x]
return sq.flagged
}
func (b *board) remaining() int {
return b.bugCount - b.flagCount
}
func squareString(sq square) string {
if !sq.shown {
return "?"
} else if sq.bug {
return "*"
} else if sq.near == 0 {
return " "
}
return fmt.Sprintf("%d", sq.near)
}
func (b *board) String() string {
buf := strings.Builder{}
for y := 0; y < b.height; y++ {
for x := 0; x < b.width; x++ {
sq := b.bugs[y][x]
buf.WriteString(squareString(sq))
}
buf.WriteByte('\n')
}
return buf.String()
}
func newBoard(height, width int) *board {
rand.Seed(time.Now().Unix())
return &board{height: height, width: width}
}

111
gui_fyne_learn/bugs/board_test.go

@ -0,0 +1,111 @@
package bugs
import (
"testing"
"github.com/stretchr/testify/assert"
)
func countMines(b *board) int {
count := 0
for y := 0; y < b.height; y++ {
for x := 0; x < b.width; x++ {
item := b.bugs[y][x]
if item.bug {
count++
}
}
}
return count
}
func TestBoard(t *testing.T) {
b := newBoard(10, 10)
assert.Equal(t, 10, b.width)
b.load(0)
assert.Equal(t, 10, len(b.bugs))
assert.Equal(t, 10, len(b.bugs[0]))
assert.Equal(t, 0, b.bugs[2][2].near)
assert.Equal(t, false, b.bugs[2][2].shown)
}
func TestBoard_load(t *testing.T) {
b := newBoard(10, 10)
b.load(4)
assert.Equal(t, 4, countMines(b))
}
func TestBoard_setMine(t *testing.T) {
b := newBoard(3, 3)
b.load(0)
b.setMine(1, 1)
assert.True(t, b.bugs[1][1].bug)
assert.Equal(t, 1, b.bugs[0][0].near)
assert.Equal(t, 1, b.bugs[0][1].near)
assert.Equal(t, 1, b.bugs[0][2].near)
assert.Equal(t, 1, b.bugs[1][0].near)
assert.Equal(t, 1, b.bugs[1][2].near)
assert.Equal(t, 1, b.bugs[2][0].near)
assert.Equal(t, 1, b.bugs[2][1].near)
assert.Equal(t, 1, b.bugs[2][2].near)
}
func TestBoard_setMines(t *testing.T) {
b := newBoard(3, 4)
b.load(0)
b.setMine(1, 1)
b.setMine(2, 1)
assert.Equal(t, 2, countMines(b))
assert.True(t, b.bugs[1][1].bug)
assert.True(t, b.bugs[1][2].bug)
assert.Equal(t, 1, b.bugs[0][0].near)
assert.Equal(t, 2, b.bugs[0][1].near)
assert.Equal(t, 2, b.bugs[0][2].near)
assert.Equal(t, 1, b.bugs[0][3].near)
assert.Equal(t, 1, b.bugs[1][0].near)
assert.Equal(t, 1, b.bugs[1][3].near)
assert.Equal(t, 1, b.bugs[2][0].near)
assert.Equal(t, 2, b.bugs[2][1].near)
assert.Equal(t, 2, b.bugs[2][2].near)
assert.Equal(t, 1, b.bugs[2][3].near)
}
func TestBoard_remaining(t *testing.T) {
b := newBoard(3, 3)
b.load(1)
assert.Equal(t, 1, b.remaining())
b.flag(0, 0)
assert.Equal(t, 0, b.remaining())
}
func TestBoard_reveal(t *testing.T) {
b := newBoard(3, 3)
b.load(0)
b.setMine(1, 1)
b.reveal(2, 1)
assert.True(t, b.bugs[1][2].shown)
}
func TestBoard_revealCascade(t *testing.T) {
b := newBoard(3, 4)
b.load(0)
b.setMine(1, 1)
b.reveal(3, 1)
assert.True(t, b.bugs[0][3].shown)
assert.True(t, b.bugs[1][3].shown)
assert.True(t, b.bugs[2][3].shown)
}

1
gui_fyne_learn/bugs/bug.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M20 8h-2.81c-.45-.78-1.07-1.45-1.82-1.96L17 4.41 15.59 3l-2.17 2.17C12.96 5.06 12.49 5 12 5c-.49 0-.96.06-1.41.17L8.41 3 7 4.41l1.62 1.63C7.88 6.55 7.26 7.22 6.81 8H4v2h2.09c-.05.33-.09.66-.09 1v1H4v2h2v1c0 .34.04.67.09 1H4v2h2.81c1.04 1.79 2.97 3 5.19 3s4.15-1.21 5.19-3H20v-2h-2.09c.05-.33.09-.66.09-1v-1h2v-2h-2v-1c0-.34-.04-.67-.09-1H20V8zm-6 8h-4v-2h4v2zm0-4h-4v-2h4v2z"/></svg>

After

Width:  |  Height:  |  Size: 512 B

20
gui_fyne_learn/bugs/bundled.go

@ -0,0 +1,20 @@
// auto-generated
package bugs
import "fyne.io/fyne/v2"
var codeIcon = &fyne.StaticResource{
StaticName: "code.svg",
StaticContent: []byte{
60, 115, 118, 103, 32, 120, 109, 108, 110, 115, 61, 34, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46, 119, 51, 46, 111, 114, 103, 47, 50, 48, 48, 48, 47, 115, 118, 103, 34, 32, 119, 105, 100, 116, 104, 61, 34, 50, 52, 34, 32, 104, 101, 105, 103, 104, 116, 61, 34, 50, 52, 34, 32, 118, 105, 101, 119, 66, 111, 120, 61, 34, 48, 32, 48, 32, 50, 52, 32, 50, 52, 34, 62, 60, 112, 97, 116, 104, 32, 102, 105, 108, 108, 61, 34, 110, 111, 110, 101, 34, 32, 100, 61, 34, 77, 48, 32, 48, 104, 50, 52, 118, 50, 52, 72, 48, 86, 48, 122, 34, 47, 62, 60, 112, 97, 116, 104, 32, 100, 61, 34, 77, 57, 46, 52, 32, 49, 54, 46, 54, 76, 52, 46, 56, 32, 49, 50, 108, 52, 46, 54, 45, 52, 46, 54, 76, 56, 32, 54, 108, 45, 54, 32, 54, 32, 54, 32, 54, 32, 49, 46, 52, 45, 49, 46, 52, 122, 109, 53, 46, 50, 32, 48, 108, 52, 46, 54, 45, 52, 46, 54, 45, 52, 46, 54, 45, 52, 46, 54, 76, 49, 54, 32, 54, 108, 54, 32, 54, 45, 54, 32, 54, 45, 49, 46, 52, 45, 49, 46, 52, 122, 34, 47, 62, 60, 47, 115, 118, 103, 62}}
var bugIcon = &fyne.StaticResource{
StaticName: "bug.svg",
StaticContent: []byte{
60, 115, 118, 103, 32, 120, 109, 108, 110, 115, 61, 34, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46, 119, 51, 46, 111, 114, 103, 47, 50, 48, 48, 48, 47, 115, 118, 103, 34, 32, 119, 105, 100, 116, 104, 61, 34, 50, 52, 34, 32, 104, 101, 105, 103, 104, 116, 61, 34, 50, 52, 34, 32, 118, 105, 101, 119, 66, 111, 120, 61, 34, 48, 32, 48, 32, 50, 52, 32, 50, 52, 34, 62, 60, 112, 97, 116, 104, 32, 100, 61, 34, 77, 48, 32, 48, 104, 50, 52, 118, 50, 52, 72, 48, 122, 34, 32, 102, 105, 108, 108, 61, 34, 110, 111, 110, 101, 34, 47, 62, 60, 112, 97, 116, 104, 32, 100, 61, 34, 77, 50, 48, 32, 56, 104, 45, 50, 46, 56, 49, 99, 45, 46, 52, 53, 45, 46, 55, 56, 45, 49, 46, 48, 55, 45, 49, 46, 52, 53, 45, 49, 46, 56, 50, 45, 49, 46, 57, 54, 76, 49, 55, 32, 52, 46, 52, 49, 32, 49, 53, 46, 53, 57, 32, 51, 108, 45, 50, 46, 49, 55, 32, 50, 46, 49, 55, 67, 49, 50, 46, 57, 54, 32, 53, 46, 48, 54, 32, 49, 50, 46, 52, 57, 32, 53, 32, 49, 50, 32, 53, 99, 45, 46, 52, 57, 32, 48, 45, 46, 57, 54, 46, 48, 54, 45, 49, 46, 52, 49, 46, 49, 55, 76, 56, 46, 52, 49, 32, 51, 32, 55, 32, 52, 46, 52, 49, 108, 49, 46, 54, 50, 32, 49, 46, 54, 51, 67, 55, 46, 56, 56, 32, 54, 46, 53, 53, 32, 55, 46, 50, 54, 32, 55, 46, 50, 50, 32, 54, 46, 56, 49, 32, 56, 72, 52, 118, 50, 104, 50, 46, 48, 57, 99, 45, 46, 48, 53, 46, 51, 51, 45, 46, 48, 57, 46, 54, 54, 45, 46, 48, 57, 32, 49, 118, 49, 72, 52, 118, 50, 104, 50, 118, 49, 99, 48, 32, 46, 51, 52, 46, 48, 52, 46, 54, 55, 46, 48, 57, 32, 49, 72, 52, 118, 50, 104, 50, 46, 56, 49, 99, 49, 46, 48, 52, 32, 49, 46, 55, 57, 32, 50, 46, 57, 55, 32, 51, 32, 53, 46, 49, 57, 32, 51, 115, 52, 46, 49, 53, 45, 49, 46, 50, 49, 32, 53, 46, 49, 57, 45, 51, 72, 50, 48, 118, 45, 50, 104, 45, 50, 46, 48, 57, 99, 46, 48, 53, 45, 46, 51, 51, 46, 48, 57, 45, 46, 54, 54, 46, 48, 57, 45, 49, 118, 45, 49, 104, 50, 118, 45, 50, 104, 45, 50, 118, 45, 49, 99, 48, 45, 46, 51, 52, 45, 46, 48, 52, 45, 46, 54, 55, 45, 46, 48, 57, 45, 49, 72, 50, 48, 86, 56, 122, 109, 45, 54, 32, 56, 104, 45, 52, 118, 45, 50, 104, 52, 118, 50, 122, 109, 48, 45, 52, 104, 45, 52, 118, 45, 50, 104, 52, 118, 50, 122, 34, 47, 62, 60, 47, 115, 118, 103, 62}}
var flagIcon = &fyne.StaticResource{
StaticName: "flag.svg",
StaticContent: []byte{
60, 115, 118, 103, 32, 120, 109, 108, 110, 115, 61, 34, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46, 119, 51, 46, 111, 114, 103, 47, 50, 48, 48, 48, 47, 115, 118, 103, 34, 32, 119, 105, 100, 116, 104, 61, 34, 50, 52, 34, 32, 104, 101, 105, 103, 104, 116, 61, 34, 50, 52, 34, 32, 118, 105, 101, 119, 66, 111, 120, 61, 34, 48, 32, 48, 32, 50, 52, 32, 50, 52, 34, 62, 60, 112, 97, 116, 104, 32, 100, 61, 34, 77, 48, 32, 48, 104, 50, 52, 118, 50, 52, 72, 48, 122, 34, 32, 102, 105, 108, 108, 61, 34, 110, 111, 110, 101, 34, 47, 62, 60, 112, 97, 116, 104, 32, 100, 61, 34, 77, 49, 32, 50, 49, 104, 50, 50, 76, 49, 50, 32, 50, 32, 49, 32, 50, 49, 122, 109, 49, 50, 45, 51, 104, 45, 50, 118, 45, 50, 104, 50, 118, 50, 122, 109, 48, 45, 52, 104, 45, 50, 118, 45, 52, 104, 50, 118, 52, 122, 34, 47, 62, 60, 47, 115, 118, 103, 62}}

124
gui_fyne_learn/bugs/button.go

@ -0,0 +1,124 @@
package bugs
import (
"image/color"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
)
type bugRenderer struct {
icon *canvas.Image
label *canvas.Text
objects []fyne.CanvasObject
button *bugButton
}
const bugSize = 18
// MinSize calculates the minimum size of a bug button. A fixed amount.
func (b *bugRenderer) MinSize() fyne.Size {
return fyne.NewSize(bugSize+theme.Padding()*2, bugSize+theme.Padding()*2)
}
// Layout the components of the widget
func (b *bugRenderer) Layout(size fyne.Size) {
inner := size.Subtract(fyne.NewSize(theme.Padding()*2, theme.Padding()*2))
b.icon.Resize(inner)
b.icon.Move(fyne.NewPos(theme.Padding(), theme.Padding()))
textSize := size.Height * .67
textMin := fyne.MeasureText(b.label.Text, textSize, fyne.TextStyle{Bold: true})
b.label.TextSize = textSize
b.label.Resize(fyne.NewSize(size.Width, textMin.Height))
b.label.Move(fyne.NewPos(0, (size.Height-textMin.Height)/2))
}
// ApplyTheme is called when the bugButton may need to update it's look
func (b *bugRenderer) ApplyTheme() {
b.label.Color = theme.ForegroundColor()
b.Refresh()
}
func (b *bugRenderer) BackgroundColor() color.Color {
return theme.ButtonColor()
}
func (b *bugRenderer) Refresh() {
b.label.Text = b.button.text
b.icon.Hidden = b.button.icon == nil
if b.button.icon != nil {
b.icon.Resource = b.button.icon
}
b.Layout(b.button.Size())
canvas.Refresh(b.button)
}
func (b *bugRenderer) Objects() []fyne.CanvasObject {
return b.objects
}
func (b *bugRenderer) Destroy() {
}
// bugButton widget is a scalable button that has a text label and icon and triggers an event func when clicked
type bugButton struct {
widget.BaseWidget
text string
icon fyne.Resource
tap func(bool)
}
// Tapped is called when a regular tap is reported
func (b *bugButton) Tapped(ev *fyne.PointEvent) {
b.tap(true)
}
// TappedSecondary is called when an alternative tap is reported
func (b *bugButton) TappedSecondary(ev *fyne.PointEvent) {
b.tap(false)
}
func (b *bugButton) CreateRenderer() fyne.WidgetRenderer {
text := canvas.NewText(b.text, theme.ForegroundColor())
text.Alignment = fyne.TextAlignCenter
text.TextStyle.Bold = true
icon := canvas.NewImageFromResource(b.icon)
icon.FillMode = canvas.ImageFillContain
objects := []fyne.CanvasObject{
text,
icon,
}
return &bugRenderer{icon, text, objects, b}
}
// SetText allows the button label to be changed
func (b *bugButton) SetText(text string) {
b.text = text
b.Refresh()
}
// SetIcon updates the icon on a label - pass nil to hide an icon
func (b *bugButton) SetIcon(icon fyne.Resource) {
b.icon = icon
b.Refresh()
}
// newButton creates a new button widget with the specified label, themed icon and tap handler
func newButton(label string, icon fyne.Resource, tap func(bool)) *bugButton {
button := &bugButton{text: label, icon: icon, tap: tap}
button.ExtendBaseWidget(button)
return button
}

1
gui_fyne_learn/bugs/code.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"/></svg>

After

Width:  |  Height:  |  Size: 230 B

1
gui_fyne_learn/bugs/flag.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/></svg>

After

Width:  |  Height:  |  Size: 188 B

13
gui_fyne_learn/bugs/gen.sh

@ -0,0 +1,13 @@
#!/bin/sh
DIR=`dirname "$0"`
FILE=bundled.go
BIN=`go env GOPATH`/bin
cd $DIR
rm $FILE
$BIN/fyne bundle -package bugs -name codeIcon code.svg > $FILE
$BIN/fyne bundle -package bugs -name bugIcon -append bug.svg >> $FILE
$BIN/fyne bundle -package bugs -name flagIcon -append flag.svg >> $FILE

213
gui_fyne_learn/bugs/main.go

@ -0,0 +1,213 @@
package bugs
import (
"fmt"
"image/color"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
)
var bug, code, flag *theme.ThemedResource
func init() {
bug = theme.NewThemedResource(bugIcon)
code = theme.NewThemedResource(codeIcon)
flag = theme.NewThemedResource(flagIcon)
}
type gameRenderer struct {
grid *fyne.Container
header fyne.CanvasObject
game *game
}
func (g *gameRenderer) MinSize() fyne.Size {
return g.grid.MinSize().Add(fyne.NewSize(0, g.header.MinSize().Height))
}
func (g *gameRenderer) Layout(size fyne.Size) {
headerHeight := g.header.MinSize().Height
g.header.Resize(fyne.NewSize(size.Width, headerHeight))
g.grid.Move(fyne.NewPos(0, headerHeight)) // TODO why ignored?
gridSize := size.Subtract(fyne.NewSize(0, headerHeight))
g.grid.Layout.Layout(g.grid.Objects, gridSize)
}
func (g *gameRenderer) ApplyTheme() {
}
func (g *gameRenderer) BackgroundColor() color.Color {
return theme.BackgroundColor()
}
func (g *gameRenderer) Refresh() {
canvas.Refresh(g.grid)
}
func (g *gameRenderer) Objects() []fyne.CanvasObject {
return []fyne.CanvasObject{g.grid, g.header}
}
func (g *gameRenderer) Destroy() {
}
type game struct {
widget.BaseWidget
board *board
remain *widget.Label
grid *fyne.Container
window fyne.Window
}
func (g *game) refreshSquare(x, y int) {
if x < 0 || y < 0 || x >= g.board.width || y >= g.board.height {
return
}
sq := g.board.bugs[y][x]
i := y*g.board.width + x
button := g.grid.Objects[i].(*bugButton)
if sq.flagged {
if button.icon == flag {
return
}
button.icon = flag
button.text = ""
} else if !sq.shown {
if button.icon == code {
return
}
button.icon = code
button.text = ""
} else if sq.bug {
if button.icon == bug {
return
}
button.icon = bug
button.text = ""
} else if button.icon == nil {
return
} else {
button.icon = nil
button.text = squareString(sq)
}
button.Refresh()
}
func (g *game) refreshAround(xp, yp, d int) {
x, y := xp-d, yp-d
for ; x < xp+d; x++ {
g.refreshSquare(x, y)
}
for ; y < yp+d; y++ {
g.refreshSquare(x, y)
}
for ; x > xp-d; x-- {
g.refreshSquare(x, y)
}
for ; y > yp-d; y-- {
g.refreshSquare(x, y)
}
}
func (g *game) refreshFrom(x, y int) {
g.refreshSquare(x, y)
for i := 1; i < int(fyne.Max(float32(g.board.width), float32(g.board.height))); i++ {
g.refreshAround(x, y, i)
}
}
func (g *game) CreateRenderer() fyne.WidgetRenderer {
renderer := &gameRenderer{game: g}
title := widget.NewLabel("Hunt bugs!")
g.remain = widget.NewLabel("")
g.updateRemain()
renderer.header = container.NewBorder(nil, nil, title, g.remain)
var buttons []fyne.CanvasObject
for y := 0; y < g.board.height; y++ {
for x := 0; x < g.board.width; x++ {
xx, yy := x, y
buttons = append(buttons, newButton("", code, func(reveal bool) {
if reveal {
g.squareReveal(xx, yy)
} else {
g.squareFlagged(xx, yy)
}
g.updateRemain()
}))
}
}
renderer.grid = container.NewGridWithColumns(g.board.width, buttons...)
g.grid = renderer.grid
return renderer
}
func (g *game) squareReveal(x, y int) {
if g.board.flagged(x, y) {
return
}
g.board.reveal(x, y)
g.refreshFrom(x, y)
}
func (g *game) squareFlagged(x, y int) {
g.board.flag(x, y)
g.refreshSquare(x, y)
}
func (g *game) loseCallback(yes bool) {
if !yes {
return
}
g.board.load(40)
g.updateRemain()
g.refreshFrom(g.board.width/2, g.board.height/2)
}
func (g *game) win() {
dialog.ShowInformation("You won!", "Congratulations, you found all the bugs", g.window)
}
func (g *game) lose() {
dialog.ShowConfirm("You lost!", "You hit a bug and lost the game, try again?", g.loseCallback, g.window)
}
func (g *game) updateRemain() {
g.remain.SetText(fmt.Sprintf("remaining: %d", g.board.remaining()))
}
func newGame(f *board) *game {
g := &game{board: f}
g.ExtendBaseWidget(g)
return g
}
// Show starts a new bugs game
func Show(win fyne.Window) fyne.CanvasObject {
b := newBoard(20, 14)
game := newGame(b)
b.win = game.win
b.lose = game.lose
b.load(40)
game.window = win
return game
}

132
gui_fyne_learn/clock/clock.go

@ -0,0 +1,132 @@
package clock
import (
"math"
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/theme"
)
type clockLayout struct {
hour, minute, second *canvas.Line
hourDot, secondDot, face *canvas.Circle
canvas fyne.CanvasObject
stop bool
}
func (c *clockLayout) rotate(hand *canvas.Line, middle fyne.Position, facePosition float64, offset, length float32) {
rotation := math.Pi * 2 / 60 * facePosition
x2 := length * float32(math.Sin(rotation))
y2 := -length * float32(math.Cos(rotation))
offX := float32(0)
offY := float32(0)
if offset > 0 {
offX += offset * float32(math.Sin(rotation))
offY += -offset * float32(math.Cos(rotation))
}
hand.Position1 = fyne.NewPos(middle.X+offX, middle.Y+offY)
hand.Position2 = fyne.NewPos(middle.X+offX+x2, middle.Y+offY+y2)
hand.Refresh()
}
func (c *clockLayout) Layout(_ []fyne.CanvasObject, size fyne.Size) {
diameter := fyne.Min(size.Width, size.Height)
radius := diameter / 2
dotRadius := radius / 12
smallDotRadius := dotRadius / 8
stroke := diameter / 40
midStroke := diameter / 90
smallStroke := diameter / 200
size = fyne.NewSize(diameter, diameter)
middle := fyne.NewPos(size.Width/2, size.Height/2)
topleft := fyne.NewPos(middle.X-radius, middle.Y-radius)
c.face.Resize(size)
c.face.Move(topleft)
c.hour.StrokeWidth = stroke
c.rotate(c.hour, middle, float64((time.Now().Hour()%12)*5)+(float64(time.Now().Minute())/12), dotRadius, radius/2)
c.minute.StrokeWidth = midStroke
c.rotate(c.minute, middle, float64(time.Now().Minute())+(float64(time.Now().Second())/60), dotRadius, radius*.9)
c.second.StrokeWidth = smallStroke
c.rotate(c.second, middle, float64(time.Now().Second()), 0, radius-3)
c.hourDot.StrokeWidth = stroke
c.hourDot.Resize(fyne.NewSize(dotRadius*2, dotRadius*2))
c.hourDot.Move(fyne.NewPos(middle.X-dotRadius, middle.Y-dotRadius))
c.secondDot.StrokeWidth = smallStroke
c.secondDot.Resize(fyne.NewSize(smallDotRadius*2, smallDotRadius*2))
c.secondDot.Move(fyne.NewPos(middle.X-smallDotRadius, middle.Y-smallDotRadius))
c.face.StrokeWidth = smallStroke
}
func (c *clockLayout) MinSize(_ []fyne.CanvasObject) fyne.Size {
return fyne.NewSize(200, 200)
}
func (c *clockLayout) render() *fyne.Container {
c.hourDot = &canvas.Circle{StrokeColor: theme.ForegroundColor(), StrokeWidth: 5}
c.secondDot = &canvas.Circle{StrokeColor: theme.PrimaryColor(), StrokeWidth: 3}
c.face = &canvas.Circle{StrokeColor: theme.ForegroundColor(), StrokeWidth: 1}
c.hour = &canvas.Line{StrokeColor: theme.ForegroundColor(), StrokeWidth: 5}
c.minute = &canvas.Line{StrokeColor: theme.ForegroundColor(), StrokeWidth: 3}
c.second = &canvas.Line{StrokeColor: theme.PrimaryColor(), StrokeWidth: 1}
container := container.NewWithoutLayout(c.hourDot, c.secondDot, c.face, c.hour, c.minute, c.second)
container.Layout = c
c.canvas = container
return container
}
func (c *clockLayout) animate(co fyne.CanvasObject) {
tick := time.NewTicker(time.Second)
go func() {
for !c.stop {
c.Layout(nil, co.Size())
canvas.Refresh(c.canvas)
<-tick.C
}
}()
}
func (c *clockLayout) applyTheme(_ fyne.Settings) {
c.hourDot.StrokeColor = theme.ForegroundColor()
c.secondDot.StrokeColor = theme.PrimaryColor()
c.face.StrokeColor = theme.ForegroundColor()
c.hour.StrokeColor = theme.ForegroundColor()
c.minute.StrokeColor = theme.ForegroundColor()
c.second.StrokeColor = theme.PrimaryColor()
}
// Show loads a clock example window for the specified app context
func Show(win fyne.Window) fyne.CanvasObject {
clock := &clockLayout{}
//clockWindow.SetOnClosed(func() {
// clock.stop = true
//})
content := clock.render()
go clock.animate(content)
listener := make(chan fyne.Settings)
fyne.CurrentApp().Settings().AddChangeListener(listener)
go func() {
for {
settings := <-listener
clock.applyTheme(settings)
}
}()
return content
}

10
gui_fyne_learn/data.go

File diff suppressed because one or more lines are too long

123
gui_fyne_learn/fractal/main.go

@ -0,0 +1,123 @@
package fractal
import (
"image/color"
"math"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/theme"
)
type fractal struct {
currIterations uint
currScale, currX, currY float64
window fyne.Window
canvas fyne.CanvasObject
}
func (f *fractal) Layout(objects []fyne.CanvasObject, size fyne.Size) {
f.canvas.Resize(size)
}
func (f *fractal) MinSize(objects []fyne.CanvasObject) fyne.Size {
return fyne.NewSize(320, 240)
}
//lint:ignore U1000 See TODO inside the .Show() method.
func (f *fractal) refresh() {
if f.currScale >= 1.0 {
f.currIterations = 100
} else {
f.currIterations = uint(100 * (1 + math.Pow((math.Log10(1/f.currScale)), 1.25)))
}
f.window.Canvas().Refresh(f.canvas)
}
func (f *fractal) scaleChannel(c float64, start, end uint32) uint8 {
if end >= start {
return (uint8)(c*float64(uint8(end-start))) + uint8(start)
}
return (uint8)((1-c)*float64(uint8(start-end))) + uint8(end)
}
func (f *fractal) scaleColor(c float64, start, end color.Color) color.Color {
r1, g1, b1, _ := start.RGBA()
r2, g2, b2, _ := end.RGBA()
return color.RGBA{f.scaleChannel(c, r1, r2), f.scaleChannel(c, g1, g2), f.scaleChannel(c, b1, b2), 0xff}
}
func (f *fractal) mandelbrot(px, py, w, h int) color.Color {
drawScale := 3.5 * f.currScale
aspect := (float64(h) / float64(w))
cRe := ((float64(px)/float64(w))-0.5)*drawScale + f.currX
cIm := ((float64(py)/float64(w))-(0.5*aspect))*drawScale - f.currY
var i uint
var x, y, xsq, ysq float64
for i = 0; i < f.currIterations && (xsq+ysq <= 4); i++ {
xNew := float64(xsq-ysq) + cRe
y = 2*x*y + cIm
x = xNew
xsq = x * x
ysq = y * y
}
if i == f.currIterations {
return theme.BackgroundColor()
}
mu := (float64(i) / float64(f.currIterations))
c := math.Sin((mu / 2) * math.Pi)
return f.scaleColor(c, theme.PrimaryColor(), theme.ForegroundColor())
}
//lint:ignore U1000 See TODO inside the .Show() method.
func (f *fractal) fractalRune(r rune) {
if r == '+' {
f.currScale /= 1.1
} else if r == '-' {
f.currScale *= 1.1
}
f.refresh()
}
//lint:ignore U1000 See TODO inside the .Show() method.
func (f *fractal) fractalKey(ev *fyne.KeyEvent) {
delta := f.currScale * 0.2
if ev.Name == fyne.KeyUp {
f.currY -= delta
} else if ev.Name == fyne.KeyDown {
f.currY += delta
} else if ev.Name == fyne.KeyLeft {
f.currX += delta
} else if ev.Name == fyne.KeyRight {
f.currX -= delta
}
f.refresh()
}
// Show loads a Mandelbrot fractal example window for the specified app context
func Show(win fyne.Window) fyne.CanvasObject {
fractal := &fractal{window: win}
fractal.canvas = canvas.NewRasterWithPixels(fractal.mandelbrot)
fractal.currIterations = 100
fractal.currScale = 1.0
fractal.currX = -0.75
fractal.currY = 0.0
return container.New(fractal, fractal.canvas)
// TODO: Register, and unregister, these keys:
//window.Canvas().SetOnTypedRune(fractal.fractalRune)
//window.Canvas().SetOnTypedKey(fractal.fractalKey)
}

10
gui_fyne_learn/go.mod

@ -0,0 +1,10 @@
module github.com/fyne-io/examples
go 1.12
require (
fyne.io/fyne/v2 v2.1.0
github.com/stretchr/testify v1.6.1
golang.org/x/image v0.0.0-20200618115811-c13761719519 // indirect
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect
)

93
gui_fyne_learn/go.sum

@ -0,0 +1,93 @@
fyne.io/fyne/v2 v2.1.0 h1:qzdkaXL/UpmMtG4FlsX9xMZ0Q93CRzLxkoiSXyplP/I=
fyne.io/fyne/v2 v2.1.0/go.mod h1:c1vwI38Ebd0dAdxVa6H1Pj6/+cK1xtDy61+I31g+s14=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod h1:7uhhqiBaR4CpN0k9rMjOtjpcfGd6DG2m04zQxKnWQ0I=
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 h1:FDqhDm7pcsLhhWl1QtD8vlzI4mm59llRvNzrFg6/LAA=
github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3/go.mod h1:CzM2G82Q9BDUvMTGHnXf/6OExw/Dz2ivDj48nVg7Lg8=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f h1:s0O46d8fPwk9kU4k1jj76wBquMVETx7uveQD9MCIQoU=
github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f/go.mod h1:wjpnOv6ONl2SuJSxqCPVaPZibGFdSci9HFocT9qtVYM=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210410170116-ea3d685f79fb h1:T6gaWBvRzJjuOrdCtg8fXXjKai2xSDqWTcKFUPuw8Tw=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210410170116-ea3d685f79fb/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff h1:W71vTCKoxtdXgnm1ECDFkfQnpdqAO00zzGXLA5yaEX8=
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff/go.mod h1:wfqRWLHRBsRgkp5dmbG56SA0DmVtwrF5N3oPdI8t+Aw=
github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMHVoNcyXYq9otUupF7/h/2tmHlhrS2zw7ZVvUqc=
github.com/josephspurrier/goversioninfo v0.0.0-20200309025242-14b0ab84c6ca/go.mod h1:eJTEwMjXb7kZ633hO3Ln9mBUCOjX2+FlTljvpl9SYdE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lucor/goinfo v0.0.0-20210802170112-c078a2b0f08b/go.mod h1:PRq09yoB+Q2OJReAmwzKivcYyremnibWGbK7WfftHzc=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564 h1:HunZiaEKNGVdhTRQOVpMmj5MQnGnv+e8uZNu3xFLgyM=
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564/go.mod h1:afMbS0qvv1m5tfENCwnOdZGOF8RGR/FsZ7bvBxQGZG4=
github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9 h1:m59mIOBO4kfcNCEzJNy71UkeF4XIx2EVmL9KLwDQdmM=
github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9/go.mod h1:mvWM0+15UqyrFKqdRjY6LuAVJR0HOVhJlEgZ5JWtSWU=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.3.8 h1:Nw158Q8QN+CPgTmVRByhVwapp8Mm1e2blinhmx4wx5E=
github.com/yuin/goldmark v1.3.8/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20200618115811-c13761719519 h1:1e2ufUJNM3lCHEY5jIgac/7UTjd6cgJNdatjPdFWf34=
golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

BIN
gui_fyne_learn/img/bugs.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
gui_fyne_learn/img/clock-dark.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
gui_fyne_learn/img/clock-light.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
gui_fyne_learn/img/fractal.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

BIN
gui_fyne_learn/img/icon/bug.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

27
gui_fyne_learn/img/icon/bundled.go

File diff suppressed because one or more lines are too long

BIN
gui_fyne_learn/img/icon/clock.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

BIN
gui_fyne_learn/img/icon/fractal.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

14
gui_fyne_learn/img/icon/gen.sh

@ -0,0 +1,14 @@
#!/bin/sh
DIR=`dirname "$0"`
FILE=bundled.go
BIN=`go env GOPATH`/bin
cd $DIR
rm $FILE
$BIN/fyne bundle -package icon -name bugBitmap bug.png > $FILE
$BIN/fyne bundle -package icon -append -name clockBitmap clock.png >> $FILE
$BIN/fyne bundle -package icon -append -name fractalBitmap fractal.png >> $FILE
$BIN/fyne bundle -package icon -append -name xkcdBitmap xkcd.png >> $FILE

13
gui_fyne_learn/img/icon/icon.go

@ -0,0 +1,13 @@
package icon
// BugBitmap an icon for the bug example
var BugBitmap = bugBitmap
// ClockBitmap an icon for the clock example
var ClockBitmap = clockBitmap
// FractalBitmap an icon for the fractal example
var FractalBitmap = fractalBitmap
// XKCDBitmap an icon for the XKCD example
var XKCDBitmap = xkcdBitmap

BIN
gui_fyne_learn/img/icon/xkcd.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

BIN
gui_fyne_learn/img/xkcd.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 496 KiB

71
gui_fyne_learn/main.go

@ -0,0 +1,71 @@
//go:generate fyne bundle -o data.go Icon.png
package main
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"github.com/fyne-io/examples/bugs"
"github.com/fyne-io/examples/clock"
"github.com/fyne-io/examples/fractal"
"github.com/fyne-io/examples/img/icon"
"github.com/fyne-io/examples/tictactoe"
"github.com/fyne-io/examples/xkcd"
)
type appInfo struct {
name string
icon fyne.Resource
canv bool
run func(fyne.Window) fyne.CanvasObject
}
var apps = []appInfo{
{"Bugs", icon.BugBitmap, false, bugs.Show},
{"XKCD", icon.XKCDBitmap, false, xkcd.Show},
{"Clock", icon.ClockBitmap, true, clock.Show},
{"Fractal", icon.FractalBitmap, true, fractal.Show},
{"Tic Tac Toe", theme.RadioButtonIcon(), true, tictactoe.Show},
}
func main() {
a := app.New()
a.SetIcon(resourceIconPng)
content := container.NewMax()
w := a.NewWindow("Examples")
appList := widget.NewList(
func() int {
return len(apps)
},
func() fyne.CanvasObject {
icon := &canvas.Image{}
label := widget.NewLabel("Text Editor")
labelHeight := label.MinSize().Height
icon.SetMinSize(fyne.NewSize(labelHeight, labelHeight))
return container.NewBorder(nil, nil, icon, nil,
label)
},
func(id widget.ListItemID, obj fyne.CanvasObject) {
img := obj.(*fyne.Container).Objects[1].(*canvas.Image)
text := obj.(*fyne.Container).Objects[0].(*widget.Label)
img.Resource = apps[id].icon
img.Refresh()
text.SetText(apps[id].name)
})
appList.OnSelected = func(id widget.ListItemID) {
content.Objects = []fyne.CanvasObject{apps[id].run(w)}
}
split := container.NewHSplit(appList, content)
split.Offset = 0.1
w.SetContent(split)
w.Resize(fyne.NewSize(480, 360))
w.ShowAndRun()
}

101
gui_fyne_learn/tictactoe/board.go

@ -0,0 +1,101 @@
package tictactoe
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
)
type board struct {
pieces [3][3]uint8
turn uint8
finished bool
}
func (b *board) result() uint8 {
// Check for a win in the diagonal direction from top left to bottom right.
if b.pieces[0][0] != 0 && b.pieces[0][0] == b.pieces[1][1] && b.pieces[1][1] == b.pieces[2][2] {
return b.pieces[0][0]
}
// Check for a win in the diagonal direction from bottom left to top right.
if b.pieces[0][2] != 0 && b.pieces[0][2] == b.pieces[1][1] && b.pieces[1][1] == b.pieces[2][0] {
return b.pieces[0][2]
}
for i := range b.pieces {
// Check for a win in the horizontal direction.
if b.pieces[i][0] != 0 && b.pieces[i][0] == b.pieces[i][1] && b.pieces[i][1] == b.pieces[i][2] {
return b.pieces[i][0]
}
// Check for a win in the vertical direction.
if b.pieces[0][i] != 0 && b.pieces[0][i] == b.pieces[1][i] && b.pieces[1][i] == b.pieces[2][i] {
return b.pieces[0][i]
}
}
return 0
}
func (b *board) newClick(row, column int) {
b.pieces[row][column] = b.turn%2 + 1
if b.turn > 3 && b.turn < 8 {
winner := b.result()
if winner == 0 {
return
}
number := string(winner + 48) // Number 1 is ascii #49 and 2 is ascii #50.
dialog.ShowInformation("Player "+number+" has won!", "Congratulations to player "+number+" for winning.", fyne.CurrentApp().Driver().AllWindows()[0])
b.finished = true
} else if b.turn == 8 {
dialog.ShowInformation("It is a tie!", "Nobody has won. Better luck next time.", fyne.CurrentApp().Driver().AllWindows()[0])
b.finished = true
}
}
func (b *board) Reset() {
for i := range b.pieces {
b.pieces[i][0] = 0
b.pieces[i][1] = 0
b.pieces[i][2] = 0
}
b.finished = false
b.turn = 0
}
type boardIcon struct {
widget.Icon
board *board
row, column int
}
func (i *boardIcon) Tapped(ev *fyne.PointEvent) {
if i.board.pieces[i.row][i.column] != 0 || i.board.finished {
return
}
if i.board.turn%2 == 0 {
i.SetResource(theme.RadioButtonIcon())
} else {
i.SetResource(theme.CancelIcon())
}
i.board.newClick(i.row, i.column)
i.board.turn++
}
func (i *boardIcon) Reset() {
i.SetResource(theme.ViewFullScreenIcon())
}
func newBoardIcon(row, column int, board *board) *boardIcon {
i := &boardIcon{board: board, row: row, column: column}
i.SetResource(theme.ViewFullScreenIcon())
i.ExtendBaseWidget(i)
return i
}

30
gui_fyne_learn/tictactoe/grid.go

@ -0,0 +1,30 @@
package tictactoe
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
)
// Show loads a tic-tac-toe example window for the specified app context
func Show(win fyne.Window) fyne.CanvasObject {
board := &board{}
grid := container.NewGridWithColumns(3)
for r := 0; r < 3; r++ {
for c := 0; c < 3; c++ {
grid.Add(newBoardIcon(r, c, board))
}
}
reset := widget.NewButtonWithIcon("Reset Board", theme.ViewRefreshIcon(), func() {
for i := range grid.Objects {
grid.Objects[i].(*boardIcon).Reset()
}
board.Reset()
})
return container.NewBorder(reset, nil, nil, nil, grid)
}

166
gui_fyne_learn/xkcd/main.go

@ -0,0 +1,166 @@
package xkcd
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"math/rand"
"net/http"
"os"
"reflect"
"strconv"
"strings"
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/widget"
)
// XKCD is an app to get xkcd images and display them
type XKCD struct {
ID int `json:"num"`
Title string `json:"title"`
Day string `json:"day"`
Month string `json:"month"`
Year string `json:"year"`
Link string `json:"link"`
SafeTitle string `json:"safe_title"`
Transcript string `json:"transcript"`
News string `json:"news"`
Alt string `json:"alt"`
Img string `json:"img"`
image *canvas.Image
iDEntry *widget.Entry
labels map[string]*widget.Label
}
func (x *XKCD) newLabel(name string) *widget.Label {
w := widget.NewLabel("")
x.labels[name] = w
return w
}
// NewXKCD returns a new xkcd app
func NewXKCD() *XKCD {
rand.Seed(time.Now().UnixNano())
return &XKCD{
labels: make(map[string]*widget.Label),
}
}
// Submit will lookup the xkcd cartoon and do something useful with it
func (x *XKCD) Submit() {
// Get the ID
id, _ := strconv.Atoi(x.iDEntry.Text)
if id == 0 {
id = rand.Intn(2075)
}
resp, err := http.Get(fmt.Sprintf("https://xkcd.com/%d/info.0.json", id))
if err != nil {
return
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
data, _ := ioutil.ReadAll(resp.Body)
json.Unmarshal(data, x)
x.DataToScreen()
} else {
fmt.Println("Error getting ID", id, resp.Status, resp.StatusCode)
}
}
func (x *XKCD) downloadImage(url string) {
response, e := http.Get(url)
if e != nil {
log.Fatal(e)
}
defer response.Body.Close()
file, err := ioutil.TempFile(os.TempDir(), "xkcd.png")
if err != nil {
log.Fatal(err)
}
defer file.Close()
_, err = io.Copy(file, response.Body)
if err != nil {
log.Fatal(err)
}
x.image.File = file.Name()
canvas.Refresh(x.image)
}
// DataToScreen copies the data model to the screen
func (x *XKCD) DataToScreen() {
myType := reflect.TypeOf(x).Elem()
myValue := reflect.ValueOf(x).Elem()
for i := 0; i < myType.NumField(); i++ {
tag := myType.Field(i).Tag.Get("json")
switch tag {
case "": // not a display field
case "img": // special field for images
url := myValue.Field(i).String()
go x.downloadImage(url)
case "num":
v := myValue.Field(i).Int()
x.iDEntry.SetText(fmt.Sprintf("%d", v))
default:
v := myValue.Field(i).String()
if newline := strings.IndexAny(v, "\n.-,"); newline > -1 {
v = v[:newline] + "..."
}
x.labels[tag].SetText(v)
}
}
}
// NewForm generates a new XKCD form
func (x *XKCD) NewForm(w fyne.Window) fyne.Widget {
form := &widget.Form{}
tt := reflect.TypeOf(x).Elem()
for i := 0; i < tt.NumField(); i++ {
fld := tt.Field(i)
tag := fld.Tag.Get("json")
switch tag {
case "": // not a display field
case "img": // special field for images
// we created this in the setup
case "num": // special field for ID
entry := widget.NewEntry()
x.iDEntry = entry
form.Append(fld.Name, entry)
default:
form.Append(fld.Name, x.newLabel(tag))
}
}
return form
}
// Show starts a new xkcd widget
func Show(win fyne.Window) fyne.CanvasObject {
x := NewXKCD()
form := x.NewForm(win)
submit := widget.NewButton("Submit", func() {
x.Submit()
})
submit.Importance = widget.HighImportance
buttons := container.NewHBox(
layout.NewSpacer(),
widget.NewButton("Random", func() {
x.iDEntry.Text = ""
x.Submit()
}),
submit)
x.image = &canvas.Image{FillMode: canvas.ImageFillOriginal}
return container.NewBorder(form, buttons, nil, nil, x.image)
}
Loading…
Cancel
Save