Initial commit

This commit is contained in:
Aynakeya
2022-06-21 13:02:22 -07:00
commit 9f75839ebc
161 changed files with 18766 additions and 0 deletions

View File

@@ -0,0 +1,85 @@
package tutorials
import (
"strconv"
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/driver/desktop"
"fyne.io/fyne/v2/widget"
)
func scaleString(c fyne.Canvas) string {
return strconv.FormatFloat(float64(c.Scale()), 'f', 2, 32)
}
func texScaleString(c fyne.Canvas) string {
pixels, _ := c.PixelCoordinateForPosition(fyne.NewPos(1, 1))
texScale := float32(pixels) / c.Scale()
return strconv.FormatFloat(float64(texScale), 'f', 2, 32)
}
func prependTo(g *fyne.Container, s string) {
g.Objects = append([]fyne.CanvasObject{widget.NewLabel(s)}, g.Objects...)
g.Refresh()
}
func setScaleText(scale, tex *widget.Label, win fyne.Window) {
for scale.Visible() {
scale.SetText(scaleString(win.Canvas()))
tex.SetText(texScaleString(win.Canvas()))
time.Sleep(time.Second)
}
}
// advancedScreen loads a panel that shows details and settings that are a bit
// more detailed than normally needed.
func advancedScreen(win fyne.Window) fyne.CanvasObject {
scale := widget.NewLabel("")
tex := widget.NewLabel("")
screen := widget.NewCard("Screen info", "", widget.NewForm(
&widget.FormItem{Text: "Scale", Widget: scale},
&widget.FormItem{Text: "Texture Scale", Widget: tex},
))
go setScaleText(scale, tex, win)
label := widget.NewLabel("Just type...")
generic := container.NewVBox()
desk := container.NewVBox()
genericCard := widget.NewCard("", "Generic", container.NewVScroll(generic))
deskCard := widget.NewCard("", "Desktop", container.NewVScroll(desk))
win.Canvas().SetOnTypedRune(func(r rune) {
prependTo(generic, "Rune: "+string(r))
})
win.Canvas().SetOnTypedKey(func(ev *fyne.KeyEvent) {
prependTo(generic, "Key : "+string(ev.Name))
})
if deskCanvas, ok := win.Canvas().(desktop.Canvas); ok {
deskCanvas.SetOnKeyDown(func(ev *fyne.KeyEvent) {
prependTo(desk, "KeyDown: "+string(ev.Name))
})
deskCanvas.SetOnKeyUp(func(ev *fyne.KeyEvent) {
prependTo(desk, "KeyUp : "+string(ev.Name))
})
}
return container.NewHBox(
container.NewVBox(screen,
widget.NewButton("Custom Theme", func() {
fyne.CurrentApp().Settings().SetTheme(newCustomTheme())
}),
widget.NewButton("Fullscreen", func() {
win.SetFullScreen(!win.FullScreen())
}),
),
container.NewBorder(label, nil, nil, nil,
container.NewGridWithColumns(2, genericCard, deskCard),
),
)
}

View File

@@ -0,0 +1,143 @@
package tutorials
import (
"image/color"
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
)
func makeAnimationScreen(_ fyne.Window) fyne.CanvasObject {
curves := makeAnimationCurves()
curves.Move(fyne.NewPos(0, 140+theme.Padding()))
return fyne.NewContainerWithoutLayout(makeAnimationCanvas(), curves)
}
func makeAnimationCanvas() fyne.CanvasObject {
rect := canvas.NewRectangle(color.Black)
rect.Resize(fyne.NewSize(410, 140))
a := canvas.NewColorRGBAAnimation(theme.PrimaryColorNamed(theme.ColorBlue), theme.PrimaryColorNamed(theme.ColorGreen),
time.Second*3, func(c color.Color) {
rect.FillColor = c
canvas.Refresh(rect)
})
a.RepeatCount = fyne.AnimationRepeatForever
a.AutoReverse = true
a.Start()
var a2 *fyne.Animation
i := widget.NewIcon(theme.CheckButtonCheckedIcon())
a2 = canvas.NewPositionAnimation(fyne.NewPos(0, 0), fyne.NewPos(350, 80), time.Second*3, func(p fyne.Position) {
i.Move(p)
width := 10 + (p.X / 7)
i.Resize(fyne.NewSize(width, width))
})
a2.RepeatCount = fyne.AnimationRepeatForever
a2.AutoReverse = true
a2.Curve = fyne.AnimationLinear
a2.Start()
running := true
var toggle *widget.Button
toggle = widget.NewButton("Stop", func() {
if running {
a.Stop()
a2.Stop()
toggle.SetText("Start")
} else {
a.Start()
a2.Start()
toggle.SetText("Stop")
}
running = !running
})
toggle.Resize(toggle.MinSize())
toggle.Move(fyne.NewPos(152, 54))
return fyne.NewContainerWithoutLayout(rect, i, toggle)
}
func makeAnimationCurves() fyne.CanvasObject {
label1, box1, a1 := makeAnimationCurveItem("EaseInOut", fyne.AnimationEaseInOut, 0)
label2, box2, a2 := makeAnimationCurveItem("EaseIn", fyne.AnimationEaseIn, 30+theme.Padding())
label3, box3, a3 := makeAnimationCurveItem("EaseOut", fyne.AnimationEaseOut, 60+theme.Padding()*2)
label4, box4, a4 := makeAnimationCurveItem("Linear", fyne.AnimationLinear, 90+theme.Padding()*3)
start := widget.NewButton("Compare", func() {
a1.Start()
a2.Start()
a3.Start()
a4.Start()
})
start.Resize(start.MinSize())
start.Move(fyne.NewPos(0, 120+theme.Padding()*4))
return fyne.NewContainerWithoutLayout(label1, label2, label3, label4, box1, box2, box3, box4, start)
}
func makeAnimationCurveItem(label string, curve fyne.AnimationCurve, yOff float32) (
text *widget.Label, box fyne.CanvasObject, anim *fyne.Animation) {
text = widget.NewLabel(label)
text.Alignment = fyne.TextAlignCenter
text.Resize(fyne.NewSize(380, 30))
text.Move(fyne.NewPos(0, yOff))
box = newThemedBox()
box.Resize(fyne.NewSize(30, 30))
box.Move(fyne.NewPos(0, yOff))
anim = canvas.NewPositionAnimation(
fyne.NewPos(0, yOff), fyne.NewPos(380, yOff), time.Second, func(p fyne.Position) {
box.Move(p)
box.Refresh()
})
anim.Curve = curve
anim.AutoReverse = true
anim.RepeatCount = 1
return
}
// themedBox is a simple box that change its background color according
// to the selected theme
type themedBox struct {
widget.BaseWidget
}
func newThemedBox() *themedBox {
b := &themedBox{}
b.ExtendBaseWidget(b)
return b
}
func (b *themedBox) CreateRenderer() fyne.WidgetRenderer {
b.ExtendBaseWidget(b)
bg := canvas.NewRectangle(theme.ForegroundColor())
return &themedBoxRenderer{bg: bg, objects: []fyne.CanvasObject{bg}}
}
type themedBoxRenderer struct {
bg *canvas.Rectangle
objects []fyne.CanvasObject
}
func (r *themedBoxRenderer) Destroy() {
}
func (r *themedBoxRenderer) Layout(size fyne.Size) {
r.bg.Resize(size)
}
func (r *themedBoxRenderer) MinSize() fyne.Size {
return r.bg.MinSize()
}
func (r *themedBoxRenderer) Objects() []fyne.CanvasObject {
return r.objects
}
func (r *themedBoxRenderer) Refresh() {
r.bg.FillColor = theme.ForegroundColor()
r.bg.Refresh()
}

View File

@@ -0,0 +1,111 @@
package tutorials
import (
"fmt"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/data/binding"
"fyne.io/fyne/v2/widget"
)
func bindingScreen(_ fyne.Window) fyne.CanvasObject {
f := 0.2
data := binding.BindFloat(&f)
label := widget.NewLabelWithData(binding.FloatToStringWithFormat(data, "Float value: %0.2f"))
entry := widget.NewEntryWithData(binding.FloatToString(data))
floats := container.NewGridWithColumns(2, label, entry)
slide := widget.NewSliderWithData(0, 1, data)
slide.Step = 0.01
bar := widget.NewProgressBarWithData(data)
buttons := container.NewGridWithColumns(4,
widget.NewButton("0%", func() {
data.Set(0)
}),
widget.NewButton("30%", func() {
data.Set(0.3)
}),
widget.NewButton("70%", func() {
data.Set(0.7)
}),
widget.NewButton("100%", func() {
data.Set(1)
}))
boolData := binding.NewBool()
check := widget.NewCheckWithData("Check me!", boolData)
checkLabel := widget.NewLabelWithData(binding.BoolToString(boolData))
checkEntry := widget.NewEntryWithData(binding.BoolToString(boolData))
checks := container.NewGridWithColumns(3, check, checkLabel, checkEntry)
item := container.NewVBox(floats, slide, bar, buttons, widget.NewSeparator(), checks, widget.NewSeparator())
dataList := binding.BindFloatList(&[]float64{0.1, 0.2, 0.3})
button := widget.NewButton("Append", func() {
dataList.Append(float64(dataList.Length()+1) / 10)
})
list := widget.NewListWithData(dataList,
func() fyne.CanvasObject {
return container.NewBorder(nil, nil, nil, widget.NewButton("+", nil),
widget.NewLabel("item x.y"))
},
func(item binding.DataItem, obj fyne.CanvasObject) {
f := item.(binding.Float)
text := obj.(*fyne.Container).Objects[0].(*widget.Label)
text.Bind(binding.FloatToStringWithFormat(f, "item %0.1f"))
btn := obj.(*fyne.Container).Objects[1].(*widget.Button)
btn.OnTapped = func() {
val, _ := f.Get()
_ = f.Set(val + 1)
}
})
formStruct := struct {
Name, Email string
Subscribe bool
}{}
formData := binding.BindStruct(&formStruct)
form := newFormWithData(formData)
form.OnSubmit = func() {
fmt.Println("Struct:\n", formStruct)
}
listPanel := container.NewBorder(nil, button, nil, nil, list)
return container.NewBorder(item, nil, nil, nil, container.NewGridWithColumns(2, listPanel, form))
}
func newFormWithData(data binding.DataMap) *widget.Form {
keys := data.Keys()
items := make([]*widget.FormItem, len(keys))
for i, k := range keys {
data, err := data.GetItem(k)
if err != nil {
items[i] = widget.NewFormItem(k, widget.NewLabel(err.Error()))
}
items[i] = widget.NewFormItem(k, createBoundItem(data))
}
return widget.NewForm(items...)
}
func createBoundItem(v binding.DataItem) fyne.CanvasObject {
switch val := v.(type) {
case binding.Bool:
return widget.NewCheckWithData("", val)
case binding.Float:
s := widget.NewSliderWithData(0, 1, val)
s.Step = 0.01
return s
case binding.Int:
return widget.NewEntryWithData(binding.IntToString(val))
case binding.String:
return widget.NewEntryWithData(val)
default:
return widget.NewLabel("")
}
}

View File

@@ -0,0 +1,49 @@
package tutorials
import (
"image/color"
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
)
func rgbGradient(x, y, w, h int) color.Color {
g := int(float32(x) / float32(w) * float32(255))
b := int(float32(y) / float32(h) * float32(255))
return color.NRGBA{uint8(255 - b), uint8(g), uint8(b), 0xff}
}
// canvasScreen loads a graphics example panel for the demo app
func canvasScreen(_ fyne.Window) fyne.CanvasObject {
gradient := canvas.NewHorizontalGradient(color.NRGBA{0x80, 0, 0, 0xff}, color.NRGBA{0, 0x80, 0, 0xff})
go func() {
for {
time.Sleep(time.Second)
gradient.Angle += 45
if gradient.Angle >= 360 {
gradient.Angle -= 360
}
canvas.Refresh(gradient)
}
}()
return fyne.NewContainerWithLayout(layout.NewGridWrapLayout(fyne.NewSize(90, 90)),
canvas.NewImageFromResource(theme.FyneLogo()),
&canvas.Rectangle{FillColor: color.NRGBA{0x80, 0, 0, 0xff},
StrokeColor: color.NRGBA{0xff, 0xff, 0xff, 0xff},
StrokeWidth: 1},
&canvas.Line{StrokeColor: color.NRGBA{0, 0, 0x80, 0xff}, StrokeWidth: 5},
&canvas.Circle{StrokeColor: color.NRGBA{0, 0, 0x80, 0xff},
FillColor: color.NRGBA{0x30, 0x30, 0x30, 0x60},
StrokeWidth: 2},
canvas.NewText("Text", color.NRGBA{0, 0x80, 0, 0xff}),
canvas.NewRasterWithPixels(rgbGradient),
gradient,
canvas.NewRadialGradient(color.NRGBA{0x80, 0, 0, 0xff}, color.NRGBA{0, 0x80, 0x80, 0xff}),
)
}

View File

@@ -0,0 +1,119 @@
package tutorials
import (
"fmt"
"strconv"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
)
// collectionScreen loads a tab panel for collection widgets
func collectionScreen(_ fyne.Window) fyne.CanvasObject {
content := container.NewVBox(
widget.NewLabelWithStyle("func Length() int", fyne.TextAlignLeading, fyne.TextStyle{Monospace: true}),
widget.NewLabelWithStyle("func CreateItem() fyne.CanvasObject", fyne.TextAlignLeading, fyne.TextStyle{Monospace: true}),
widget.NewLabelWithStyle("func UpdateItem(ListItemID, fyne.CanvasObject)", fyne.TextAlignLeading, fyne.TextStyle{Monospace: true}),
widget.NewLabelWithStyle("func OnSelected(ListItemID)", fyne.TextAlignLeading, fyne.TextStyle{Monospace: true}),
widget.NewLabelWithStyle("func OnUnselected(ListItemID)", fyne.TextAlignLeading, fyne.TextStyle{Monospace: true}))
return container.NewCenter(content)
}
func makeListTab(_ fyne.Window) fyne.CanvasObject {
data := make([]string, 1000)
for i := range data {
data[i] = "Test Item " + strconv.Itoa(i)
}
icon := widget.NewIcon(nil)
label := widget.NewLabel("Select An Item From The List")
hbox := container.NewHBox(icon, label)
list := widget.NewList(
func() int {
return len(data)
},
func() fyne.CanvasObject {
return container.NewHBox(widget.NewIcon(theme.DocumentIcon()), widget.NewLabel("Template Object"))
},
func(id widget.ListItemID, item fyne.CanvasObject) {
item.(*fyne.Container).Objects[1].(*widget.Label).SetText(data[id])
},
)
list.OnSelected = func(id widget.ListItemID) {
label.SetText(data[id])
icon.SetResource(theme.DocumentIcon())
}
list.OnUnselected = func(id widget.ListItemID) {
label.SetText("Select An Item From The List")
icon.SetResource(nil)
}
list.Select(125)
return container.NewHSplit(list, container.NewCenter(hbox))
}
func makeTableTab(_ fyne.Window) fyne.CanvasObject {
t := widget.NewTable(
func() (int, int) { return 500, 150 },
func() fyne.CanvasObject {
return widget.NewLabel("Cell 000, 000")
},
func(id widget.TableCellID, cell fyne.CanvasObject) {
label := cell.(*widget.Label)
switch id.Col {
case 0:
label.SetText(fmt.Sprintf("%d", id.Row+1))
case 1:
label.SetText("A longer cell")
default:
label.SetText(fmt.Sprintf("Cell %d, %d", id.Row+1, id.Col+1))
}
})
t.SetColumnWidth(0, 34)
t.SetColumnWidth(1, 102)
return t
}
func makeTreeTab(_ fyne.Window) fyne.CanvasObject {
data := map[string][]string{
"": {"A"},
"A": {"B", "D", "H", "J", "L", "O", "P", "S", "V"},
"B": {"C"},
"C": {"abc"},
"D": {"E"},
"E": {"F", "G"},
"F": {"adef"},
"G": {"adeg"},
"H": {"I"},
"I": {"ahi"},
"O": {"ao"},
"P": {"Q"},
"Q": {"R"},
"R": {"apqr"},
"S": {"T"},
"T": {"U"},
"U": {"astu"},
"V": {"W"},
"W": {"X"},
"X": {"Y"},
"Y": {"Z"},
"Z": {"avwxyz"},
}
tree := widget.NewTreeWithStrings(data)
tree.OnSelected = func(id string) {
fmt.Println("Tree node selected:", id)
}
tree.OnUnselected = func(id string) {
fmt.Println("Tree node unselected:", id)
}
tree.OpenBranch("A")
tree.OpenBranch("D")
tree.OpenBranch("E")
tree.OpenBranch("L")
tree.OpenBranch("M")
return tree
}

View File

@@ -0,0 +1,154 @@
package tutorials
import (
"fmt"
"image/color"
"strconv"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
)
// containerScreen loads a tab panel for containers
func containerScreen(_ fyne.Window) fyne.CanvasObject {
content := container.NewBorder(
widget.NewLabelWithStyle("Top", fyne.TextAlignCenter, fyne.TextStyle{}),
widget.NewLabelWithStyle("Bottom", fyne.TextAlignCenter, fyne.TextStyle{}),
widget.NewLabel("Left"),
widget.NewLabel("Right"),
widget.NewLabel("Border Container"))
return container.NewCenter(content)
}
func makeAppTabsTab(_ fyne.Window) fyne.CanvasObject {
tabs := container.NewAppTabs(
container.NewTabItem("Tab 1", widget.NewLabel("Content of tab 1")),
container.NewTabItem("Tab 2 bigger", widget.NewLabel("Content of tab 2")),
container.NewTabItem("Tab 3", widget.NewLabel("Content of tab 3")),
)
for i := 4; i <= 12; i++ {
tabs.Append(container.NewTabItem(fmt.Sprintf("Tab %d", i), widget.NewLabel(fmt.Sprintf("Content of tab %d", i))))
}
locations := makeTabLocationSelect(tabs.SetTabLocation)
return container.NewBorder(locations, nil, nil, nil, tabs)
}
func makeBorderLayout(_ fyne.Window) fyne.CanvasObject {
top := makeCell()
bottom := makeCell()
left := makeCell()
right := makeCell()
middle := widget.NewLabelWithStyle("BorderLayout", fyne.TextAlignCenter, fyne.TextStyle{})
return container.NewBorder(top, bottom, left, right, middle)
}
func makeBoxLayout(_ fyne.Window) fyne.CanvasObject {
top := makeCell()
bottom := makeCell()
middle := widget.NewLabel("BoxLayout")
center := makeCell()
right := makeCell()
col := container.NewVBox(top, middle, bottom)
return container.NewHBox(col, center, right)
}
func makeButtonList(count int) []fyne.CanvasObject {
var items []fyne.CanvasObject
for i := 1; i <= count; i++ {
index := i // capture
items = append(items, widget.NewButton("Button "+strconv.Itoa(index), func() {
fmt.Println("Tapped", index)
}))
}
return items
}
func makeCell() fyne.CanvasObject {
rect := canvas.NewRectangle(&color.NRGBA{128, 128, 128, 255})
rect.SetMinSize(fyne.NewSize(30, 30))
return rect
}
func makeCenterLayout(_ fyne.Window) fyne.CanvasObject {
middle := widget.NewButton("CenterLayout", func() {})
return container.NewCenter(middle)
}
func makeDocTabsTab(_ fyne.Window) fyne.CanvasObject {
tabs := container.NewDocTabs(
container.NewTabItem("Doc 1", widget.NewLabel("Content of document 1")),
container.NewTabItem("Doc 2 bigger", widget.NewLabel("Content of document 2")),
container.NewTabItem("Doc 3", widget.NewLabel("Content of document 3")),
)
i := 3
tabs.CreateTab = func() *container.TabItem {
i++
return container.NewTabItem(fmt.Sprintf("Doc %d", i), widget.NewLabel(fmt.Sprintf("Content of document %d", i)))
}
locations := makeTabLocationSelect(tabs.SetTabLocation)
return container.NewBorder(locations, nil, nil, nil, tabs)
}
func makeGridLayout(_ fyne.Window) fyne.CanvasObject {
box1 := makeCell()
box2 := widget.NewLabel("Grid")
box3 := makeCell()
box4 := makeCell()
return container.NewGridWithColumns(2,
box1, box2, box3, box4)
}
func makeScrollTab(_ fyne.Window) fyne.CanvasObject {
hlist := makeButtonList(20)
vlist := makeButtonList(50)
horiz := container.NewHScroll(container.NewHBox(hlist...))
vert := container.NewVScroll(container.NewVBox(vlist...))
return container.NewAdaptiveGrid(2,
container.NewBorder(horiz, nil, nil, nil, vert),
makeScrollBothTab())
}
func makeScrollBothTab() fyne.CanvasObject {
logo := canvas.NewImageFromResource(theme.FyneLogo())
logo.SetMinSize(fyne.NewSize(800, 800))
scroll := container.NewScroll(logo)
scroll.Resize(fyne.NewSize(400, 400))
return scroll
}
func makeSplitTab(_ fyne.Window) fyne.CanvasObject {
left := widget.NewMultiLineEntry()
left.Wrapping = fyne.TextWrapWord
left.SetText("Long text is looooooooooooooong")
right := container.NewVSplit(
widget.NewLabel("Label"),
widget.NewButton("Button", func() { fmt.Println("button tapped!") }),
)
return container.NewHSplit(container.NewVScroll(left), right)
}
func makeTabLocationSelect(callback func(container.TabLocation)) *widget.Select {
locations := widget.NewSelect([]string{"Top", "Bottom", "Leading", "Trailing"}, func(s string) {
callback(map[string]container.TabLocation{
"Top": container.TabLocationTop,
"Bottom": container.TabLocationBottom,
"Leading": container.TabLocationLeading,
"Trailing": container.TabLocationTrailing,
}[s])
})
locations.SetSelected("Top")
return locations
}

View File

@@ -0,0 +1,150 @@
package tutorials
import (
"fyne.io/fyne/v2"
)
// Tutorial defines the data structure for a tutorial
type Tutorial struct {
Title, Intro string
View func(w fyne.Window) fyne.CanvasObject
}
var (
// Tutorials defines the metadata for each tutorial
Tutorials = map[string]Tutorial{
"welcome": {"Welcome", "", welcomeScreen},
"canvas": {"Canvas",
"See the canvas capabilities.",
canvasScreen,
},
"animations": {"Animations",
"See how to animate components.",
makeAnimationScreen,
},
"icons": {"Theme Icons",
"Browse the embedded icons.",
iconScreen,
},
"containers": {"Containers",
"Containers group other widgets and canvas objects, organising according to their layout.\n" +
"Standard containers are illustrated in this section, but developers can also provide custom " +
"layouts using the fyne.NewContainerWithLayout() constructor.",
containerScreen,
},
"apptabs": {"AppTabs",
"A container to help divide up an application into functional areas.",
makeAppTabsTab,
},
"border": {"Border",
"A container that positions items around a central content.",
makeBorderLayout,
},
"box": {"Box",
"A container arranges items in horizontal or vertical list.",
makeBoxLayout,
},
"center": {"Center",
"A container to that centers child elements.",
makeCenterLayout,
},
"doctabs": {"DocTabs",
"A container to display a single document from a set of many.",
makeDocTabsTab,
},
"grid": {"Grid",
"A container that arranges all items in a grid.",
makeGridLayout,
},
"split": {"Split",
"A split container divides the container in two pieces that the user can resize.",
makeSplitTab,
},
"scroll": {"Scroll",
"A container that provides scrolling for it's content.",
makeScrollTab,
},
"widgets": {"Widgets",
"In this section you can see the features available in the toolkit widget set.\n" +
"Expand the tree on the left to browse the individual tutorial elements.",
widgetScreen,
},
"accordion": {"Accordion",
"Expand or collapse content panels.",
makeAccordionTab,
},
"button": {"Button",
"Simple widget for user tap handling.",
makeButtonTab,
},
"card": {"Card",
"Group content and widgets.",
makeCardTab,
},
"entry": {"Entry",
"Different ways to use the entry widget.",
makeEntryTab,
},
"form": {"Form",
"Gathering input widgets for data submission.",
makeFormTab,
},
"input": {"Input",
"A collection of widgets for user input.",
makeInputTab,
},
"text": {"Text",
"Text handling widgets.",
makeTextTab,
},
"toolbar": {"Toolbar",
"A row of shortcut icons for common tasks.",
makeToolbarTab,
},
"progress": {"Progress",
"Show duration or the need to wait for a task.",
makeProgressTab,
},
"collections": {"Collections",
"Collection widgets provide an efficient way to present lots of content.\n" +
"The List, Table, and Tree provide a cache and re-use mechanism that make it possible to scroll through thousands of elements.\n" +
"Use this for large data sets or for collections that can expand as users scroll.",
collectionScreen,
},
"list": {"List",
"A vertical arrangement of cached elements with the same styling.",
makeListTab,
},
"table": {"Table",
"A two dimensional cached collection of cells.",
makeTableTab,
},
"tree": {"Tree",
"A tree based arrangement of cached elements with the same styling.",
makeTreeTab,
},
"dialogs": {"Dialogs",
"Work with dialogs.",
dialogScreen,
},
"windows": {"Windows",
"Window function demo.",
windowScreen,
},
"binding": {"Data Binding",
"Connecting widgets to a data source.",
bindingScreen},
"advanced": {"Advanced",
"Debug and advanced information.",
advancedScreen,
},
}
// TutorialIndex defines how our tutorials should be laid out in the index tree
TutorialIndex = map[string][]string{
"": {"welcome", "canvas", "animations", "icons", "widgets", "collections", "containers", "dialogs", "windows", "binding", "advanced"},
"collections": {"list", "table", "tree"},
"containers": {"apptabs", "border", "box", "center", "doctabs", "grid", "scroll", "split"},
"widgets": {"accordion", "button", "card", "entry", "form", "input", "progress", "text", "toolbar"},
}
)

View File

@@ -0,0 +1,185 @@
package tutorials
import (
"errors"
"fmt"
"image/color"
"io/ioutil"
"log"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/data/validation"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/storage"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
)
func confirmCallback(response bool) {
fmt.Println("Responded with", response)
}
func colorPicked(c color.Color, w fyne.Window) {
log.Println("Color picked:", c)
rectangle := canvas.NewRectangle(c)
size := 2 * theme.IconInlineSize()
rectangle.SetMinSize(fyne.NewSize(size, size))
dialog.ShowCustom("Color Picked", "Ok", rectangle, w)
}
// dialogScreen loads demos of the dialogs we support
func dialogScreen(win fyne.Window) fyne.CanvasObject {
return container.NewVScroll(container.NewVBox(
widget.NewButton("Info", func() {
dialog.ShowInformation("Information", "You should know this thing...", win)
}),
widget.NewButton("Error", func() {
err := errors.New("a dummy error message")
dialog.ShowError(err, win)
}),
widget.NewButton("Confirm", func() {
cnf := dialog.NewConfirm("Confirmation", "Are you enjoying this demo?", confirmCallback, win)
cnf.SetDismissText("Nah")
cnf.SetConfirmText("Oh Yes!")
cnf.Show()
}),
widget.NewButton("File Open With Filter (.jpg or .png)", func() {
fd := dialog.NewFileOpen(func(reader fyne.URIReadCloser, err error) {
if err != nil {
dialog.ShowError(err, win)
return
}
if reader == nil {
log.Println("Cancelled")
return
}
imageOpened(reader)
}, win)
fd.SetFilter(storage.NewExtensionFileFilter([]string{".png", ".jpg", ".jpeg"}))
fd.Show()
}),
widget.NewButton("File Save", func() {
dialog.ShowFileSave(func(writer fyne.URIWriteCloser, err error) {
if err != nil {
dialog.ShowError(err, win)
return
}
if writer == nil {
log.Println("Cancelled")
return
}
fileSaved(writer, win)
}, win)
}),
widget.NewButton("Folder Open", func() {
dialog.ShowFolderOpen(func(list fyne.ListableURI, err error) {
if err != nil {
dialog.ShowError(err, win)
return
}
if list == nil {
log.Println("Cancelled")
return
}
children, err := list.List()
if err != nil {
dialog.ShowError(err, win)
return
}
out := fmt.Sprintf("Folder %s (%d children):\n%s", list.Name(), len(children), list.String())
dialog.ShowInformation("Folder Open", out, win)
}, win)
}),
widget.NewButton("Color Picker", func() {
picker := dialog.NewColorPicker("Pick a Color", "What is your favorite color?", func(c color.Color) {
colorPicked(c, win)
}, win)
picker.Show()
}),
widget.NewButton("Advanced Color Picker", func() {
picker := dialog.NewColorPicker("Pick a Color", "What is your favorite color?", func(c color.Color) {
colorPicked(c, win)
}, win)
picker.Advanced = true
picker.Show()
}),
widget.NewButton("Form Dialog (Login Form)", func() {
username := widget.NewEntry()
username.Validator = validation.NewRegexp(`^[A-Za-z0-9_-]+$`, "username can only contain letters, numbers, '_', and '-'")
password := widget.NewPasswordEntry()
password.Validator = validation.NewRegexp(`^[A-Za-z0-9_-]+$`, "password can only contain letters, numbers, '_', and '-'")
remember := false
items := []*widget.FormItem{
widget.NewFormItem("Username", username),
widget.NewFormItem("Password", password),
widget.NewFormItem("Remember me", widget.NewCheck("", func(checked bool) {
remember = checked
})),
}
dialog.ShowForm("Login...", "Log In", "Cancel", items, func(b bool) {
if !b {
return
}
var rememberText string
if remember {
rememberText = "and remember this login"
}
log.Println("Please Authenticate", username.Text, password.Text, rememberText)
}, win)
}),
))
}
func imageOpened(f fyne.URIReadCloser) {
if f == nil {
log.Println("Cancelled")
return
}
defer f.Close()
showImage(f)
}
func fileSaved(f fyne.URIWriteCloser, w fyne.Window) {
defer f.Close()
_, err := f.Write([]byte("Written by Fyne demo\n"))
if err != nil {
dialog.ShowError(err, w)
}
err = f.Close()
if err != nil {
dialog.ShowError(err, w)
}
log.Println("Saved to...", f.URI())
}
func loadImage(f fyne.URIReadCloser) *canvas.Image {
data, err := ioutil.ReadAll(f)
if err != nil {
fyne.LogError("Failed to load image data", err)
return nil
}
res := fyne.NewStaticResource(f.URI().Name(), data)
return canvas.NewImageFromResource(res)
}
func showImage(f fyne.URIReadCloser) {
img := loadImage(f)
if img == nil {
return
}
img.FillMode = canvas.ImageFillOriginal
w := fyne.CurrentApp().NewWindow(f.URI().Name())
w.SetContent(container.NewScroll(img))
w.Resize(fyne.NewSize(320, 240))
w.Show()
}

View File

@@ -0,0 +1,195 @@
package tutorials
import (
"image/color"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
)
type iconInfo struct {
name string
icon fyne.Resource
}
type browser struct {
current int
icons []iconInfo
name *widget.Select
icon *widget.Icon
}
func (b *browser) setIcon(index int) {
if index < 0 || index > len(b.icons)-1 {
return
}
b.current = index
b.name.SetSelected(b.icons[index].name)
b.icon.SetResource(b.icons[index].icon)
}
// iconScreen loads a panel that shows the various icons available in Fyne
func iconScreen(_ fyne.Window) fyne.CanvasObject {
b := &browser{}
b.icons = loadIcons()
prev := widget.NewButtonWithIcon("", theme.NavigateBackIcon(), func() {
b.setIcon(b.current - 1)
})
next := widget.NewButtonWithIcon("", theme.NavigateNextIcon(), func() {
b.setIcon(b.current + 1)
})
b.name = widget.NewSelect(iconList(b.icons), func(name string) {
for i, icon := range b.icons {
if icon.name == name {
if b.current != i {
b.setIcon(i)
}
break
}
}
})
b.name.SetSelected(b.icons[b.current].name)
buttons := container.NewHBox(prev, next)
bar := container.NewBorder(nil, nil, buttons, nil, b.name)
background := canvas.NewRasterWithPixels(checkerPattern)
background.SetMinSize(fyne.NewSize(280, 280))
b.icon = widget.NewIcon(b.icons[b.current].icon)
return fyne.NewContainerWithLayout(layout.NewBorderLayout(
bar, nil, nil, nil), bar, background, b.icon)
}
func checkerPattern(x, y, _, _ int) color.Color {
x /= 20
y /= 20
if x%2 == y%2 {
return theme.BackgroundColor()
}
return theme.ShadowColor()
}
func iconList(icons []iconInfo) []string {
ret := make([]string, len(icons))
for i, icon := range icons {
ret[i] = icon.name
}
return ret
}
func loadIcons() []iconInfo {
return []iconInfo{
{"CancelIcon", theme.CancelIcon()},
{"ConfirmIcon", theme.ConfirmIcon()},
{"DeleteIcon", theme.DeleteIcon()},
{"SearchIcon", theme.SearchIcon()},
{"SearchReplaceIcon", theme.SearchReplaceIcon()},
{"CheckButtonIcon", theme.CheckButtonIcon()},
{"CheckButtonCheckedIcon", theme.CheckButtonCheckedIcon()},
{"RadioButtonIcon", theme.RadioButtonIcon()},
{"RadioButtonCheckedIcon", theme.RadioButtonCheckedIcon()},
{"ColorAchromaticIcon", theme.ColorAchromaticIcon()},
{"ColorChromaticIcon", theme.ColorChromaticIcon()},
{"ColorPaletteIcon", theme.ColorPaletteIcon()},
{"ContentAddIcon", theme.ContentAddIcon()},
{"ContentRemoveIcon", theme.ContentRemoveIcon()},
{"ContentClearIcon", theme.ContentClearIcon()},
{"ContentCutIcon", theme.ContentCutIcon()},
{"ContentCopyIcon", theme.ContentCopyIcon()},
{"ContentPasteIcon", theme.ContentPasteIcon()},
{"ContentRedoIcon", theme.ContentRedoIcon()},
{"ContentUndoIcon", theme.ContentUndoIcon()},
{"InfoIcon", theme.InfoIcon()},
{"ErrorIcon", theme.ErrorIcon()},
{"QuestionIcon", theme.QuestionIcon()},
{"WarningIcon", theme.WarningIcon()},
{"DocumentIcon", theme.DocumentIcon()},
{"DocumentCreateIcon", theme.DocumentCreateIcon()},
{"DocumentPrintIcon", theme.DocumentPrintIcon()},
{"DocumentSaveIcon", theme.DocumentSaveIcon()},
{"FileIcon", theme.FileIcon()},
{"FileApplicationIcon", theme.FileApplicationIcon()},
{"FileAudioIcon", theme.FileAudioIcon()},
{"FileImageIcon", theme.FileImageIcon()},
{"FileTextIcon", theme.FileTextIcon()},
{"FileVideoIcon", theme.FileVideoIcon()},
{"FolderIcon", theme.FolderIcon()},
{"FolderNewIcon", theme.FolderNewIcon()},
{"FolderOpenIcon", theme.FolderOpenIcon()},
{"ComputerIcon", theme.ComputerIcon()},
{"HomeIcon", theme.HomeIcon()},
{"HelpIcon", theme.HelpIcon()},
{"HistoryIcon", theme.HistoryIcon()},
{"SettingsIcon", theme.SettingsIcon()},
{"StorageIcon", theme.StorageIcon()},
{"DownloadIcon", theme.DownloadIcon()},
{"UploadIcon", theme.UploadIcon()},
{"ViewFullScreenIcon", theme.ViewFullScreenIcon()},
{"ViewRestoreIcon", theme.ViewRestoreIcon()},
{"ViewRefreshIcon", theme.ViewRefreshIcon()},
{"VisibilityIcon", theme.VisibilityIcon()},
{"VisibilityOffIcon", theme.VisibilityOffIcon()},
{"ZoomFitIcon", theme.ZoomFitIcon()},
{"ZoomInIcon", theme.ZoomInIcon()},
{"ZoomOutIcon", theme.ZoomOutIcon()},
{"MoreHorizontalIcon", theme.MoreHorizontalIcon()},
{"MoreVerticalIcon", theme.MoreVerticalIcon()},
{"MoveDownIcon", theme.MoveDownIcon()},
{"MoveUpIcon", theme.MoveUpIcon()},
{"NavigateBackIcon", theme.NavigateBackIcon()},
{"NavigateNextIcon", theme.NavigateNextIcon()},
{"Menu", theme.MenuIcon()},
{"MenuExpand", theme.MenuExpandIcon()},
{"MenuDropDown", theme.MenuDropDownIcon()},
{"MenuDropUp", theme.MenuDropUpIcon()},
{"MailAttachmentIcon", theme.MailAttachmentIcon()},
{"MailComposeIcon", theme.MailComposeIcon()},
{"MailForwardIcon", theme.MailForwardIcon()},
{"MailReplyIcon", theme.MailReplyIcon()},
{"MailReplyAllIcon", theme.MailReplyAllIcon()},
{"MailSendIcon", theme.MailSendIcon()},
{"MediaFastForward", theme.MediaFastForwardIcon()},
{"MediaFastRewind", theme.MediaFastRewindIcon()},
{"MediaPause", theme.MediaPauseIcon()},
{"MediaPlay", theme.MediaPlayIcon()},
{"MediaStop", theme.MediaStopIcon()},
{"MediaRecord", theme.MediaRecordIcon()},
{"MediaReplay", theme.MediaReplayIcon()},
{"MediaSkipNext", theme.MediaSkipNextIcon()},
{"MediaSkipPrevious", theme.MediaSkipPreviousIcon()},
{"VolumeDown", theme.VolumeDownIcon()},
{"VolumeMute", theme.VolumeMuteIcon()},
{"VolumeUp", theme.VolumeUpIcon()},
{"AccountIcon", theme.AccountIcon()},
{"LoginIcon", theme.LoginIcon()},
{"LogoutIcon", theme.LogoutIcon()},
{"ListIcon", theme.ListIcon()},
{"GridIcon", theme.GridIcon()},
}
}

View File

@@ -0,0 +1,72 @@
package tutorials
import (
"image/color"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/theme"
)
var (
purple = &color.NRGBA{R: 128, G: 0, B: 128, A: 255}
orange = &color.NRGBA{R: 198, G: 123, B: 0, A: 255}
grey = &color.Gray{Y: 123}
)
// customTheme is a simple demonstration of a bespoke theme loaded by a Fyne app.
type customTheme struct {
}
func (customTheme) Color(c fyne.ThemeColorName, _ fyne.ThemeVariant) color.Color {
switch c {
case theme.ColorNameBackground:
return purple
case theme.ColorNameButton, theme.ColorNameDisabled:
return color.Black
case theme.ColorNamePlaceHolder, theme.ColorNameScrollBar:
return grey
case theme.ColorNamePrimary, theme.ColorNameHover, theme.ColorNameFocus:
return orange
case theme.ColorNameShadow:
return &color.RGBA{R: 0xcc, G: 0xcc, B: 0xcc, A: 0xcc}
default:
return color.White
}
}
func (customTheme) Font(style fyne.TextStyle) fyne.Resource {
return theme.DarkTheme().Font(style)
}
func (customTheme) Icon(n fyne.ThemeIconName) fyne.Resource {
return theme.DefaultTheme().Icon(n)
}
func (customTheme) Size(s fyne.ThemeSizeName) float32 {
switch s {
case theme.SizeNamePadding:
return 8
case theme.SizeNameInlineIcon:
return 20
case theme.SizeNameScrollBar:
return 10
case theme.SizeNameScrollBarSmall:
return 5
case theme.SizeNameText:
return 18
case theme.SizeNameHeadingText:
return 30
case theme.SizeNameSubHeadingText:
return 25
case theme.SizeNameCaptionText:
return 15
case theme.SizeNameInputBorder:
return 1
default:
return 0
}
}
func newCustomTheme() fyne.Theme {
return &customTheme{}
}

View File

@@ -0,0 +1,42 @@
package tutorials
import (
"net/url"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/cmd/fyne_demo/data"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
)
func parseURL(urlStr string) *url.URL {
link, err := url.Parse(urlStr)
if err != nil {
fyne.LogError("Could not parse URL", err)
}
return link
}
func welcomeScreen(_ fyne.Window) fyne.CanvasObject {
logo := canvas.NewImageFromResource(data.FyneScene)
logo.FillMode = canvas.ImageFillContain
if fyne.CurrentDevice().IsMobile() {
logo.SetMinSize(fyne.NewSize(171, 125))
} else {
logo.SetMinSize(fyne.NewSize(228, 167))
}
return container.NewCenter(container.NewVBox(
widget.NewLabelWithStyle("Welcome to the Fyne toolkit demo app", fyne.TextAlignCenter, fyne.TextStyle{Bold: true}),
logo,
container.NewHBox(
widget.NewHyperlink("fyne.io", parseURL("https://fyne.io/")),
widget.NewLabel("-"),
widget.NewHyperlink("documentation", parseURL("https://developer.fyne.io/")),
widget.NewLabel("-"),
widget.NewHyperlink("sponsor", parseURL("https://fyne.io/sponsor/")),
),
))
}

View File

@@ -0,0 +1,420 @@
package tutorials
import (
"fmt"
"image/color"
"net/url"
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/data/validation"
"fyne.io/fyne/v2/driver/mobile"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
)
const (
loremIpsum = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque quis consectetur nisi. Suspendisse id interdum felis.
Sed egestas eget tellus eu pharetra. Praesent pulvinar sed massa id placerat. Etiam sem libero, semper vitae consequat ut, volutpat id mi.
Mauris volutpat pellentesque convallis. Curabitur rutrum venenatis orci nec ornare. Maecenas quis pellentesque neque.
Aliquam consectetur dapibus nulla, id maximus odio ultrices ac. Sed luctus at felis sed faucibus. Cras leo augue, congue in velit ut, mattis rhoncus lectus.
Praesent viverra, mauris ut ullamcorper semper, leo urna auctor lectus, vitae vehicula mi leo quis lorem.
Nullam condimentum, massa at tempor feugiat, metus enim lobortis velit, eget suscipit eros ipsum quis tellus. Aenean fermentum diam vel felis dictum semper.
Duis nisl orci, tincidunt ut leo quis, luctus vehicula diam. Sed velit justo, congue id augue eu, euismod dapibus lacus. Proin sit amet imperdiet sapien.
Mauris erat urna, fermentum et quam rhoncus, fringilla consequat ante. Vivamus consectetur molestie odio, ac rutrum erat finibus a.
Suspendisse id maximus felis. Sed mauris odio, mattis eget mi eu, consequat tempus purus.`
)
var (
progress *widget.ProgressBar
fprogress *widget.ProgressBar
infProgress *widget.ProgressBarInfinite
endProgress chan interface{}
)
func makeAccordionTab(_ fyne.Window) fyne.CanvasObject {
link, err := url.Parse("https://fyne.io/")
if err != nil {
fyne.LogError("Could not parse URL", err)
}
ac := widget.NewAccordion(
widget.NewAccordionItem("A", widget.NewHyperlink("One", link)),
widget.NewAccordionItem("B", widget.NewLabel("Two")),
&widget.AccordionItem{
Title: "C",
Detail: widget.NewLabel("Three"),
},
)
ac.Append(widget.NewAccordionItem("D", &widget.Entry{Text: "Four"}))
return ac
}
func makeButtonTab(_ fyne.Window) fyne.CanvasObject {
disabled := widget.NewButton("Disabled", func() {})
disabled.Disable()
shareItem := fyne.NewMenuItem("Share via", nil)
shareItem.ChildMenu = fyne.NewMenu("",
fyne.NewMenuItem("Twitter", func() { fmt.Println("context menu Share->Twitter") }),
fyne.NewMenuItem("Reddit", func() { fmt.Println("context menu Share->Reddit") }),
)
menuLabel := newContextMenuButton("tap me for pop-up menu with submenus", fyne.NewMenu("",
fyne.NewMenuItem("Copy", func() { fmt.Println("context menu copy") }),
shareItem,
))
return container.NewVBox(
widget.NewButton("Button (text only)", func() { fmt.Println("tapped text button") }),
widget.NewButtonWithIcon("Button (text & leading icon)", theme.ConfirmIcon(), func() { fmt.Println("tapped text & leading icon button") }),
&widget.Button{
Alignment: widget.ButtonAlignLeading,
Text: "Button (leading-aligned, text only)",
OnTapped: func() { fmt.Println("tapped leading-aligned, text only button") },
},
&widget.Button{
Alignment: widget.ButtonAlignTrailing,
IconPlacement: widget.ButtonIconTrailingText,
Text: "Button (trailing-aligned, text & trailing icon)",
Icon: theme.ConfirmIcon(),
OnTapped: func() { fmt.Println("tapped trailing-aligned, text & trailing icon button") },
},
disabled,
layout.NewSpacer(),
layout.NewSpacer(),
menuLabel,
layout.NewSpacer(),
)
}
func makeCardTab(_ fyne.Window) fyne.CanvasObject {
card1 := widget.NewCard("Book a table", "Which time suits?",
widget.NewRadioGroup([]string{"6:30pm", "7:00pm", "7:45pm"}, func(string) {}))
card2 := widget.NewCard("With media", "No content, with image", nil)
card2.Image = canvas.NewImageFromResource(theme.FyneLogo())
card3 := widget.NewCard("Title 3", "Another card", widget.NewLabel("Content"))
return container.NewGridWithColumns(2, container.NewVBox(card1, card3),
container.NewVBox(card2))
}
func makeEntryTab(_ fyne.Window) fyne.CanvasObject {
entry := widget.NewEntry()
entry.SetPlaceHolder("Entry")
entryDisabled := widget.NewEntry()
entryDisabled.SetText("Entry (disabled)")
entryDisabled.Disable()
entryValidated := newNumEntry()
entryValidated.SetPlaceHolder("Must contain a number")
entryMultiLine := widget.NewMultiLineEntry()
entryMultiLine.SetPlaceHolder("MultiLine Entry")
return container.NewVBox(
entry,
entryDisabled,
entryValidated,
entryMultiLine)
}
func makeTextGrid() *widget.TextGrid {
grid := widget.NewTextGridFromString("TextGrid\n\tContent\nZebra")
grid.SetStyleRange(0, 4, 0, 7,
&widget.CustomTextGridStyle{BGColor: &color.NRGBA{R: 64, G: 64, B: 192, A: 128}})
grid.SetRowStyle(1, &widget.CustomTextGridStyle{BGColor: &color.NRGBA{R: 64, G: 192, B: 64, A: 128}})
white := &widget.CustomTextGridStyle{FGColor: color.White, BGColor: color.Black}
black := &widget.CustomTextGridStyle{FGColor: color.Black, BGColor: color.White}
grid.Rows[2].Cells[0].Style = white
grid.Rows[2].Cells[1].Style = black
grid.Rows[2].Cells[2].Style = white
grid.Rows[2].Cells[3].Style = black
grid.Rows[2].Cells[4].Style = white
grid.ShowLineNumbers = true
grid.ShowWhitespace = true
return grid
}
func makeTextTab(_ fyne.Window) fyne.CanvasObject {
label := widget.NewLabel("Label")
link, err := url.Parse("https://fyne.io/")
if err != nil {
fyne.LogError("Could not parse URL", err)
}
hyperlink := widget.NewHyperlink("Hyperlink", link)
entryLoremIpsum := widget.NewMultiLineEntry()
entryLoremIpsum.SetText(loremIpsum)
label.Alignment = fyne.TextAlignLeading
hyperlink.Alignment = fyne.TextAlignLeading
label.Wrapping = fyne.TextWrapWord
hyperlink.Wrapping = fyne.TextWrapWord
entryLoremIpsum.Wrapping = fyne.TextWrapWord
rich := widget.NewRichTextFromMarkdown(`
# RichText Heading
## A Sub Heading
---
* Item1 in _three_ segments
* Item2
* Item3
Normal **Bold** *Italic* [Link](https://fyne.io/) and some ` + "`Code`" + `.
This styled row should also wrap as expected, but only *when required*.
> An interesting quote here, most likely sharing some very interesting wisdom.`)
rich.Scroll = container.ScrollBoth
radioAlign := widget.NewRadioGroup([]string{"Text Alignment Leading", "Text Alignment Center", "Text Alignment Trailing"}, func(s string) {
var align fyne.TextAlign
switch s {
case "Text Alignment Leading":
align = fyne.TextAlignLeading
case "Text Alignment Center":
align = fyne.TextAlignCenter
case "Text Alignment Trailing":
align = fyne.TextAlignTrailing
}
label.Alignment = align
hyperlink.Alignment = align
for i := range rich.Segments {
if seg, ok := rich.Segments[i].(*widget.TextSegment); ok {
seg.Style.Alignment = align
}
if seg, ok := rich.Segments[i].(*widget.HyperlinkSegment); ok {
seg.Alignment = align
}
}
label.Refresh()
hyperlink.Refresh()
rich.Refresh()
})
radioAlign.SetSelected("Text Alignment Leading")
radioWrap := widget.NewRadioGroup([]string{"Text Wrapping Off", "Text Wrapping Truncate", "Text Wrapping Break", "Text Wrapping Word"}, func(s string) {
var wrap fyne.TextWrap
switch s {
case "Text Wrapping Off":
wrap = fyne.TextWrapOff
case "Text Wrapping Truncate":
wrap = fyne.TextTruncate
case "Text Wrapping Break":
wrap = fyne.TextWrapBreak
case "Text Wrapping Word":
wrap = fyne.TextWrapWord
}
label.Wrapping = wrap
hyperlink.Wrapping = wrap
entryLoremIpsum.Wrapping = wrap
rich.Wrapping = wrap
label.Refresh()
hyperlink.Refresh()
entryLoremIpsum.Refresh()
rich.Refresh()
})
radioWrap.SetSelected("Text Wrapping Word")
fixed := container.NewVBox(
container.NewHBox(
radioAlign,
layout.NewSpacer(),
radioWrap,
),
label,
hyperlink,
)
grid := makeTextGrid()
return container.NewBorder(fixed, grid, nil, nil,
container.NewGridWithRows(2, rich, entryLoremIpsum))
}
func makeInputTab(_ fyne.Window) fyne.CanvasObject {
selectEntry := widget.NewSelectEntry([]string{"Option A", "Option B", "Option C"})
selectEntry.PlaceHolder = "Type or select"
disabledCheck := widget.NewCheck("Disabled check", func(bool) {})
disabledCheck.Disable()
checkGroup := widget.NewCheckGroup([]string{"CheckGroup Item 1", "CheckGroup Item 2AAAAAAAAAAAAAA", "CheckGroup Item 3"}, func(s []string) { fmt.Println("selected", s) })
checkGroup.Horizontal = true
radio := widget.NewRadioGroup([]string{"Radio Item 1", "Radio Item 2"}, func(s string) { fmt.Println("selected", s) })
radio.Horizontal = true
disabledRadio := widget.NewRadioGroup([]string{"Disabled radio"}, func(string) {})
disabledRadio.Disable()
return container.NewVBox(
widget.NewSelect([]string{"Option 1", "Option 2", "Option 3"}, func(s string) { fmt.Println("selected", s) }),
selectEntry,
widget.NewCheck("Check", func(on bool) { fmt.Println("checked", on) }),
disabledCheck,
checkGroup,
radio,
disabledRadio,
widget.NewSlider(0, 100),
)
}
func makeProgressTab(_ fyne.Window) fyne.CanvasObject {
stopProgress()
progress = widget.NewProgressBar()
fprogress = widget.NewProgressBar()
fprogress.TextFormatter = func() string {
return fmt.Sprintf("%.2f out of %.2f", fprogress.Value, fprogress.Max)
}
infProgress = widget.NewProgressBarInfinite()
endProgress = make(chan interface{}, 1)
startProgress()
return container.NewVBox(
widget.NewLabel("Percent"), progress,
widget.NewLabel("Formatted"), fprogress,
widget.NewLabel("Infinite"), infProgress)
}
func makeFormTab(_ fyne.Window) fyne.CanvasObject {
name := widget.NewEntry()
name.SetPlaceHolder("John Smith")
email := widget.NewEntry()
email.SetPlaceHolder("test@example.com")
email.Validator = validation.NewRegexp(`\w{1,}@\w{1,}\.\w{1,4}`, "not a valid email")
password := widget.NewPasswordEntry()
password.SetPlaceHolder("Password")
disabled := widget.NewRadioGroup([]string{"Option 1", "Option 2"}, func(string) {})
disabled.Horizontal = true
disabled.Disable()
largeText := widget.NewMultiLineEntry()
form := &widget.Form{
Items: []*widget.FormItem{
{Text: "Name", Widget: name, HintText: "Your full name"},
{Text: "Email", Widget: email, HintText: "A valid email address"},
},
OnCancel: func() {
fmt.Println("Cancelled")
},
OnSubmit: func() {
fmt.Println("Form submitted")
fyne.CurrentApp().SendNotification(&fyne.Notification{
Title: "Form for: " + name.Text,
Content: largeText.Text,
})
},
}
form.Append("Password", password)
form.Append("Disabled", disabled)
form.Append("Message", largeText)
return form
}
func makeToolbarTab(_ fyne.Window) fyne.CanvasObject {
t := widget.NewToolbar(widget.NewToolbarAction(theme.MailComposeIcon(), func() { fmt.Println("New") }),
widget.NewToolbarSeparator(),
widget.NewToolbarSpacer(),
widget.NewToolbarAction(theme.ContentCutIcon(), func() { fmt.Println("Cut") }),
widget.NewToolbarAction(theme.ContentCopyIcon(), func() { fmt.Println("Copy") }),
widget.NewToolbarAction(theme.ContentPasteIcon(), func() { fmt.Println("Paste") }),
)
return container.NewBorder(t, nil, nil, nil)
}
func startProgress() {
progress.SetValue(0)
fprogress.SetValue(0)
select { // ignore stale end message
case <-endProgress:
default:
}
go func() {
end := endProgress
num := 0.0
for num < 1.0 {
time.Sleep(16 * time.Millisecond)
select {
case <-end:
return
default:
}
progress.SetValue(num)
fprogress.SetValue(num)
num += 0.002
}
progress.SetValue(1)
fprogress.SetValue(1)
// TODO make sure this resets when we hide etc...
stopProgress()
}()
infProgress.Start()
}
func stopProgress() {
if !infProgress.Running() {
return
}
infProgress.Stop()
endProgress <- struct{}{}
}
// widgetScreen shows a panel containing widget demos
func widgetScreen(_ fyne.Window) fyne.CanvasObject {
content := container.NewVBox(
widget.NewLabel("Labels"),
widget.NewButtonWithIcon("Icons", theme.HomeIcon(), func() {}),
widget.NewSlider(0, 1))
return container.NewCenter(content)
}
type contextMenuButton struct {
widget.Button
menu *fyne.Menu
}
func (b *contextMenuButton) Tapped(e *fyne.PointEvent) {
widget.ShowPopUpMenuAtPosition(b.menu, fyne.CurrentApp().Driver().CanvasForObject(b), e.AbsolutePosition)
}
func newContextMenuButton(label string, menu *fyne.Menu) *contextMenuButton {
b := &contextMenuButton{menu: menu}
b.Text = label
b.ExtendBaseWidget(b)
return b
}
type numEntry struct {
widget.Entry
}
func (n *numEntry) Keyboard() mobile.KeyboardType {
return mobile.NumberKeyboard
}
func newNumEntry() *numEntry {
e := &numEntry{}
e.ExtendBaseWidget(e)
e.Validator = validation.NewRegexp(`\d`, "Must contain a number")
return e
}

View File

@@ -0,0 +1,71 @@
package tutorials
import (
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/driver/desktop"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/widget"
)
func windowScreen(_ fyne.Window) fyne.CanvasObject {
windowGroup := container.NewVBox(
widget.NewButton("New window", func() {
w := fyne.CurrentApp().NewWindow("Hello")
w.SetContent(widget.NewLabel("Hello World!"))
w.Show()
}),
widget.NewButton("Fixed size window", func() {
w := fyne.CurrentApp().NewWindow("Fixed")
w.SetContent(fyne.NewContainerWithLayout(layout.NewCenterLayout(), widget.NewLabel("Hello World!")))
w.Resize(fyne.NewSize(240, 180))
w.SetFixedSize(true)
w.Show()
}),
widget.NewButton("Toggle between fixed/not fixed window size", func() {
w := fyne.CurrentApp().NewWindow("Toggle fixed size")
w.SetContent(fyne.NewContainerWithLayout(layout.NewCenterLayout(), widget.NewCheck("Fixed size", func(toggle bool) {
if toggle {
w.Resize(fyne.NewSize(240, 180))
}
w.SetFixedSize(toggle)
})))
w.Show()
}),
widget.NewButton("Centered window", func() {
w := fyne.CurrentApp().NewWindow("Central")
w.SetContent(fyne.NewContainerWithLayout(layout.NewCenterLayout(), widget.NewLabel("Hello World!")))
w.CenterOnScreen()
w.Show()
}))
drv := fyne.CurrentApp().Driver()
if drv, ok := drv.(desktop.Driver); ok {
windowGroup.Objects = append(windowGroup.Objects,
widget.NewButton("Splash Window (only use on start)", func() {
w := drv.CreateSplashWindow()
w.SetContent(widget.NewLabelWithStyle("Hello World!\n\nMake a splash!",
fyne.TextAlignCenter, fyne.TextStyle{Bold: true}))
w.Show()
go func() {
time.Sleep(time.Second * 3)
w.Close()
}()
}))
}
otherGroup := widget.NewCard("Other", "",
widget.NewButton("Notification", func() {
fyne.CurrentApp().SendNotification(&fyne.Notification{
Title: "Fyne Demo",
Content: "Testing notifications...",
})
}))
return container.NewVBox(widget.NewCard("Windows", "", windowGroup), otherGroup)
}